OmniAICreator/Qiita-1.07M huggingface dataset:
有料記事販売サービスBOOKERSのAPIドキュメントリニューアル、API投稿してみた

BOOKERS開発です。
「BOOKERS」( https://bookers.tech/about/ )は低手数料・記事投稿API等、有料記事投稿者に優しいサービスを目指すプラットフォームです。

BOOKERSローンチ時から提供している定番機能「投稿用API」のドキュメント( https://bookers.tech/api/docs/ )をリニューアルしたので、使ってみた記事書いてみます。是非日々の記事販売の自動化・省力化にご活用ください！
ご意見ご要望お待ちしてます！！

# API キーの取得
メニュー＞アカウント設定の下部でキーを取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3672987/8b6407a4-acfd-b6c2-9e58-54e2e61f88b3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3672987/aaeee8a9-5921-b7cd-0506-b05a33d5efeb.png)


# API投稿を試す
{token}の箇所に取得したAPIキーをコピペします。
Googleアカウントがあれば手軽にpython環境を試せるGoogle Colab( https://colab.research.google.com/?hl=ja )などで気軽に試せます。

## 記事投稿
```python
import requests, json
headers = {
    'Authorization': 'Token {token}',
    'Content-Type': 'application/json',
}
data = {"title":"API投稿やってみた","category":2,"description":"記事の概要を書きます","text":"# Markdownで記事書けます！","price":500}
requests.post('https://bookers.tech/api/postcreate/', headers=headers, data=json.dumps(data))
```

## 記事投稿（下書き）
エンドポイントが異なる以外は「記事投稿（公開）」と同様です。
```python
import requests, json
headers = {
    'Authorization': 'Token {token}',
    'Content-Type': 'application/json',
}
data = {"title":"API投稿やってみた","category":2,"description":"記事の概要を書きます","text":"# Markdownで記事書けます！","price":500}
requests.post('https://bookers.tech/api/postdraftcreate/', headers=headers, data=json.dumps(data))
```

## 記事投稿（ダウンロードコンテンツの追加）
サンプルコード(CSVを2ファイルアップロード)
{uuid}には記事のidを設定します。
```python
import requests
XLSX_MIMETYPE = "text/csv"
files = {
  'dlc_main': ({filename_main}, open("{filepath_main}", 'rb').read(), XLSX_MIMETYPE),
  'dlc_sub01': ({filename_sub01}, open("{filepath_sub01}", 'rb').read(), XLSX_MIMETYPE),
}
req = requests.Request('PATCH', f'https://bookers.tech/api/postdlcupdate/{ uuid }/', files=files)
prepped = req.prepare()
prepped.headers['Authorization'] = f'Token {token}'
requests.Session().send(prepped)
```

## 記事更新
{uuid}には記事のidを設定します。
```python
import requests, json
headers = {
    'Authorization': 'Token {token}',
    'Content-Type': 'application/json',
}
data = {"text":"記事更新してみた！"}
requests.patch('https://bookers.tech/api/api/postupdate/{uuid}/', headers=headers, data=json.dumps(data))
```

## SNSキャンペーン設定
X(旧Twitter)のリポストで無料・割引などのキャンペーンを設定できます。
{uuid}には記事のidを設定します。
```python
import requests, json
headers = {
    'Authorization': 'Token {token}',
    'Content-Type': 'application/json',
}
data = {"discount_campaign_category":"TW_RT_F","discount_campaign_tweet":"https://twitter.com/BookersOfficial/status/1733014352490553710","discount_price":0}
requests.patch('https://bookers.tech/api/postcampaignupdate/{uuid}/', headers=headers, data=json.dumps(data))
```

## マガジン追加
{post}には記事のidを設定します。
{magazine}には記事のidを設定します。
```python
import requests, json
headers = {
    'Authorization': 'Token {token}',
    'Content-Type': 'application/json',
}
data = {"post":"{post}", "magazine":"{magazine}"}
requests.post('https://bookers.tech/api/magazinepostcreate/', headers=headers, data=json.dumps(data))
```
Figmaでクラス図を手軽に描く

# 概要
Figmaを使うとクラス図を手軽に描くことができます。操作は簡単なのでFigmaを使ったことがない人でも大丈夫です。
# 本文
## クラス図の作成
[Figmaのウィジェット「EntityModeler」のページ](https://www.figma.com/community/widget/1052293548792295369/entity-modeler)に行きます。
![スクリーンショット 2024-01-14 22.25.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/1081c4dc-2f7c-28cd-6194-42becd918bee.png)
「場所を指定して開く」ボタンから「＋新しいファイル」を押すとノードが一つだけ用意された新規ファイルが作成されます。（Figmaアカウントへのログインもしくはアカウント作成が必要になるかもしれません）
![スクリーンショット 2024-01-14 22.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/381c02c9-d3d5-df3b-d1fc-f10223b892d9.png)
ノードの右上の４つの点を押すと中身を編集するためのポップアップが開かれます。1行目にEntity名を入力したら後はプラスボタンを押すごとに項目を増やしていけます。後はスクショを参考にしてクラス図を作成してみてください！
![スクリーンショット 2024-01-14 22.31.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/1c048bbb-4d09-bcc8-2296-ad75140c541f.png)
![スクリーンショット 2024-01-14 22.51.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/244f1ee7-de43-f2fd-d8a4-47f59ae52132.png)
## エクスポート
クラス図の出力したい範囲をドラッグで囲んで選択したら右クリックのメニューから「選択内容をエクスポート」を選びます。（画面左上のメインメニューボタンから「ファイル > エクスポートオプション」でもOKです）
![スクリーンショット 2024-01-14 23.01.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/ddae9087-0533-fad8-9864-ffd8ecb322d4.png)
「エクスポート」ポップアップが表示されます。ファイルの種類を選んだら背景の項目で透明にしたりもできます。
![スクリーンショット 2024-01-14 23.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/38b3b497-26c5-81ab-bb46-15b80ea78945.png)
完成したクラス図がダウンロードフォルダに出力されます。
![Entity Modeler (コミュニティ) (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/0449de11-7b23-85cd-3eaa-32496e56832b.png)
# 補足
参考にさせていただいたサイト
[Figmaで効率的にオブジェクトモデルを設計する方法](https://wentz-design.com/post/figma-how-to-create-objective-model/)
PythonでNLTK (Natural Language Toolkit)を使用する

<h1>はじめに</h1>
Pythonで自然言語生成ライブラリを使って遊んでみました。

<h1>準備するもの</h1>
<p>from pdfminer.high_level import extract_text</p>
<p>from nltk.stem import WordNetLemmatizer</p>
<p>import nltk</p>

```python:script.py

from pdfminer.high_level import extract_text
from nltk.stem import WordNetLemmatizer
import nltk

text = extract_text(r'/Users/username/Desktop/004.文字起こしOCR/Project001/RowData/サンプル.pdf')
# 改行を削除
text = text.replace('\n', '')
# 「。」で改行
text = text.replace('。', '。\n')
#print(text)


nltk.download('punkt')  # 分かち書き用
nltk.download('averaged_perceptron_tagger')  # 品詞の取得用

morph = nltk.word_tokenize(text)
print(morph)

pos = nltk.pos_tag(morph)
print(pos)  # [('Hi', 'NNP'), (',', ','), ('I', 'PRP'), ("'m", 'VBP'), ('Taro', 'JJ'), ('Yamada', 'NNP'), ('I', 'PRP'), ('woke', 'VBD'), ('up', 'RB'), ('at', 'IN'), ('8am', 'CD')]


lemmatizer = WordNetLemmatizer()
#s = "getting"
print(lemmatizer.lemmatize(text))  # getting
print(lemmatizer.lemmatize(text, pos="v"))  # get




```
Javaでカンマ付き数値文字列をBigDecimalに変換する方法

### はじめに

金融アプリケーションや精密な計算を要するJavaプログラムでは、BigDecimalの使用が欠かせません。しかし、"1,000", "10,000"のようにカンマで区切られた数値を含む文字列を直接BigDecimalに変換しようとすると、問題が発生します。今回は、カンマ付きの数値文字列をBigDecimalに変換する方法について説明します。

### BigDecimalの重要性

BigDecimalは、Javaにおける高精度の数値計算を実現するクラスです。浮動小数点数では発生しやすい丸め誤差を防ぐため、特に金融関連のアプリケーションでよく使用されます。

### カンマ付き数値文字列のBigDecimalへの変換手順

カンマが含まれる数値文字列をBigDecimalに変換するには、次のステップに従います

```ruby:java
import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        String numberWithCommas = "1,000";
        String sanitizedStr = numberWithCommas.replace(",", "");
        BigDecimal number = new BigDecimal(sanitizedStr);
        
        System.out.println("Converted BigDecimal: " + number);
    }
}
```
### まとめ
この記事では、カンマ付きの数値文字列をBigDecimalに変換する方法について説明しました。この方法は、特に金額や高精度の計算を扱う場合に有効です。Javaでの数値処理において、このような形式のデータ変換は一般的な作業なので、覚えておくと便利でしょう。
React18でのKeycloakの無限レンダリング　自分用メモ

<h1>無限レンダリングの発生</h1>
ReactアプリにKeycloak-js19を導入し、ログインを試みたところ
ログイン成功までずっとレンダリングが発生した。

色々調べていたところ、
Reactのstrictmodeが有効だと発生するようで、
解除するとレンダリングは正常に戻った。


参考文献
https://github.com/react-keycloak/react-keycloak/issues/182
【Java】Resource leak: 'sc' is never closed

# 結論
`"try-with-resources" syntax`を使う。

# ワーニングが出たコード
```:scのところでResource leak: 'sc' is never closed
package chapter1;

import java.util.Scanner;

public class Min3 {
  static int min3(int a, int b, int c) {
    System.out.println("a: " + a + "\nb: " + b + "\nc: " + c);
    int min = a;
    System.out.println(min);
    if (min > b) min = b;
    if (min > c) min = c;
    return min;
  }

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in); // ここでエラーが出る
    int a,b,c;

    System.out.println("３つの整数のうち最小値を求めます");
    System.out.println("1つ目"); a = sc.nextInt();
    System.out.println("2つ目"); b = sc.nextInt();
    System.out.println("3つ目"); c = sc.nextInt();

    System.out.println("最小値は" + min3(a, b, c) + "です。");
  }
}


```

# 原因
Scannerが閉じられていない。

# 解決
tryブロックに入れる。`"try-with-resources" syntax`というらしい。

```:java7以降
package chapter1;

import java.util.Scanner;

public class Min3 {
  static int min3(int a, int b, int c) {
    System.out.println("a: " + a + "\nb: " + b + "\nc: " + c);
    int min = a;
    System.out.println(min);
    if (min > b) min = b;
    if (min > c) min = c;
    return min;
  }

  public static void main(String[] args) {
    try (Scanner sc = new Scanner(System.in)) {
      int a,b,c;

      System.out.println("３つの整数のうち最小値を求めます");
      System.out.println("1つ目"); a = sc.nextInt();
      System.out.println("2つ目"); b = sc.nextInt();
      System.out.println("3つ目"); c = sc.nextInt();

      System.out.println("最小値は" + min3(a, b, c) + "です。");
    }
  }
}

```

```:java７以前
package chapter1;

import java.util.Scanner;

public class Min3 {
  static int min3(int a, int b, int c) {
    System.out.println("a: " + a + "\nb: " + b + "\nc: " + c);
    int min = a;
    System.out.println(min);
    if (min > b) min = b;
    if (min > c) min = c;
    return min;
  }

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    try {
      int a,b,c;
      
      System.out.println("３つの整数のうち最小値を求めます");
      System.out.println("1つ目"); a = sc.nextInt();
      System.out.println("2つ目"); b = sc.nextInt();
      System.out.println("3つ目"); c = sc.nextInt();
      
      System.out.println("最小値は" + min3(a, b, c) + "です。");
    } finally {
      sc.close();
    }
  }
}

```

# Sytem.inの場合は閉じなくてもよい
ファイルやデータベース接続している場合は、ファイルロックやデータベース接続プールの枯渇やメモリリークの恐れがあるので確実にcloseする必要がある。

とはいっても、Scannerを使う場合は閉じないとエディタがワーニングを出してくれるので、try with resourcesを使う癖をつけていた方が無難でしょうね！
ABC336をPythonで（A~E）

AtCoder Beginner Contest 336

https://atcoder.jp/contests/abc336

# A問題
oを入力された数だけ増やす
```python:A
print("L"+"o"*int(input())+"ng")
```
# B問題
$mod2$で$0$だったらカウントを増やして%2%で割る
```python:B
n = int(input())
ans = 0
while n > 0 and n % 2 == 0:
    ans += 1
    n //= 2

print(ans)
```
# C問題
5進数に変換して、各桁を2倍する
```python:C
n = int(input()) - 1

lst = []
while n > 0:
    lst.append(n % 5)
    n //= 5

ans = 0
for l_i in reversed(lst):
    ans = ans * 10 + l_i * 2

print(ans)
```
# D問題
サイズを基準に二分探索

サイズxで出来るかの判定は、
端からxまで上がりそこから下がれるかを調べる
もし、欲しい高さより低い場所aが現れたときは
それまでに条件を満たせるものは絶対になく、そのピラミッドを途中からaの高さまで上がったものが可能性のあるものの中で一番左のものになる
```python:D
n = int(input())
a = list(map(int, input().split()))

def func(x):
    now = 0
    border = 1
    up_side = True
    for i in range(n):
        if a[i] >= border:
            if up_side and border < x:
                border += 1
            else:
                up_side = False
                border -= 1
                if border <= 0:
                    return True
        else:
            up_side = True
            border = a[i] + 1

    return False


ok, ng = 1, n + 1
while abs(ok - ng) > 1:
    mid = (ok + ng) // 2
    if func(mid):
        ok = mid
    else:
        ng = mid

print(ok)
```
# E問題
~~出来なかったので、後日やります~~ 出来ました

基本は[公式解説](https://atcoder.jp/contests/abc336/editorial/9055)を参考にしました。
桁和が$p$で且つ$mod$ $p$で0になる自然数の個数を求める関数を用意して、桁和が起こりえるものすべてを計算するようにしてみました。
```python:E
def digit_dp(p):
    # 桁和がpのうち、mod pで0になるN以下の自然数の個数
    # dp[i][j][k][f] = i桁目で、桁和がj、mod pがkとなる数の個数。f=[N未満が確定したもの, Nと等しいかもしれないもの]
    dp = [[[[0] * 2 for _ in range(p + 1)] for _ in range(p + 1)]for _ in range(digit + 1)]
    dp[0][0][0][1] = 1
    for i in range(digit): # i桁目
        n = int(N[i])
        for j in range(p + 1): # 桁和がj
            for k in range(p): # mod p == k
                for x in range(10):
                    for f in range(2):
                        if j + x > p: continue # 桁和が超えたもの
                        if f and n < x: continue # Nより大きくなるもの
                        dp[i + 1][j + x][(k * 10 + x) % p][f and n == x] += dp[i][j][k][f]

    return sum(dp[-1][p][0])

N = input()
digit = len(N) # 桁

ans = 0
for i in range(1, digit * 9 + 1):
    ans += digit_dp(i)

print(ans)
```
Clean Architecture概要(中半)

[前の記事へ](https://qiita.com/sakaki_developer/items/060e7c777ade8f45fddf
)      [次の記事へ](https://qiita.com/sakaki_developer/items/7ecdeb614023696b511b
)

# はじめに
クリーンアーキテクチャの概要を説明します。

# 記事の構成
- Clean Architecture超概要編(前半)
章構成: 要約 = 手段 + 効果
- Clean Architecture概要編(中半)
章構成: 手段の説明
**今ここ👆**
- Clean Architectureソース解説編(後半)
---

# 手段
1. 各レイヤー層に分ける
2. 依存の方向性の決定: 依存性逆転の原則

![Group 6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2171762/fcb5b8be-3c33-c237-bd5a-34c9100153a0.png)

---
# 1. 各レイヤー層に分ける
各レイヤーは、アプリケーションの異なる側面を管理し、その責務を明確にします。
これにより、関心の分離が実現され、システムの柔軟性とメンテナンス性が向上します。

![Group 12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2171762/c17176c6-06c5-c6b1-5a62-f51de1e501eb.png)
---
この表では、クリーンアーキテクチャにおける各レイヤーの役割とそれに対応する具体的なコンポーネントを明確にしています。
| No | レイヤー名 (英語) | 説明 (日本語) |
| --- | --- | --- |
| 1 | Entity | アプリケーションの基本的なデータモデルやビジネスルールを定義するレイヤー。 |
| 2 | Use Case | ユーザーの行動やビジネスプロセスを具体化するユースケースを実装するレイヤー。 |
| 3 | Controller | ユーザーのリクエストを受け取り、ビジネスロジックを呼び出し、結果を処理する層。 |
| 3 | Presenter | データを加工してUIに適した形式で表示するためのロジックを担当する層。 |
| 3 | Gateway | データベースや外部サービスへのアクセスを管理する層。 |
| 4 | Device | ハードウェアやデバイス間のインタラクションを処理する層。 |
| 4 | Web | WebベースのインターフェイスやAPIを通じた外部通信を管理する層。 |
| 4 | DB | データベースとのやり取りを担当する層。 |
| 4 | UI | ユーザーインターフェースとのやり取りを担当する層。 |
| 4 | External Interfaces | 外部システムやAPIとの連携を管理する層。 |


| https://www.ogis-ri.co.jp/otc/hiroba/others/OOcolumn/single-responsibility-principle.html
---
# 2. 依存の方向性の決定: 依存性逆転の原則

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2171762/c356773d-66c5-f979-d454-662608eed838.png)

## クリーンアーキテクチャの「依存性逆転」とは
システム設計における依存関係の方向を変える概念です。通常、高レベルのビジネスロジック(Entity層)は低レベル(UI層)の実装詳細（例えばデータベースやUI）に依存しますが、クリーンアーキテクチャではこの依存関係を逆転させます。つまり、低レベルの実装が高レベルのポリシーに依存するように設計されます。これにより、ビジネスロジックが外部の変更に強くなります。

## 依存性逆転の目的
---

- **逆転の意味**: 通常、高レベルのビジネスロジックは低レベルの実装詳細に依存しますが、依存性逆転ではこの関係が逆になります。
- **実装の独立性**: 低レベルの実装（データベースやUIなど）が高レベルのビジネスルールやポリシーに依存するように設計されます。

図解すると以下のようになります。
```mermaid
graph LR
    A[通常のパターン]
    B[低レベルの実装: Entity層]
    C[高レベルのビジネスロジック: UI層] -->|依存する| B
```
```mermaid
graph TD
    D[依存性逆転の原則] 
    E[インターフェース]
    F[低レベルの実装: Infrastructure層] -->|実装する| E
    C[高レベルのビジネスロジック: Usecase層] -->|依存する| E
```

- **インターフェースの利用**: 依存性逆転は、インターフェースや抽象クラスを用いることで実現されます。
- **柔軟性の向上**: この逆転により、ビジネスロジックが具体的な実装詳細から独立し、変更に対して柔軟に対応できるようになります。


--- 

## 実装
```mermaid
graph TD
    A[IData] -->|実装| B[IDataRepository]
    B -->|継承| C[SqlDataRepository]
    D[BusinessLogic] -->|依存| B

````


```C#
// インターフェース定義
public interface IData
```

```C#
// インターフェース定義
public interface IDataRepository {
    void SaveData(string data);
}

// 低レベルの実装クラス
public class SqlDataRepository : IDataRepository {
    public void SaveData(string data) {
        // SQLデータベースにデータを保存するロジック
    }
}
```
```C#

// 高レベルビジネスロジッククラス
public class BusinessLogic {
    private readonly IDataRepository _dataRepository;

    public BusinessLogic(IDataRepository dataRepository) {
        _dataRepository = dataRepository;
    }

    public void ProcessData(string data) {
        // ビジネスロジックの処理
        _dataRepository.SaveData(data);
    }
}
```
ABC336回答メモ

# 0.はじめに
　そろそろ正月気分も抜けてきたころですが、変則？日曜開催のABCに挑戦。
　特に企業名を冠していないフラットな感じの出題だった気がします。
　A~Dを解いて20分ほど残しましたが、E以降に進めるきがしないので
　そこで試合終了でした。
　週一投稿維持のため、テスト後即投稿の術。


# 1.A - Long Loong
　辰年にちなんだ問題。あらかじめ用意したLとその間に指定された数だけの
　oをループでくっつけ、最後にngを添えて出力するだけの簡単なお仕事。
 
　https://atcoder.jp/contests/abc336/submissions/49286308


# 2. - CTZ
　2進数を取り扱う問題。
　とはいえ、B問題にしては難易度が低かったです。
　インプットしたNを2進数表示に変換し、末尾から、0の数を数えて
　0が途切れたら数えた0の数を表示して終了。
 
　https://atcoder.jp/contests/abc336/submissions/49289842


# 3.C - Even Digits
　急に難易度が上がった問題。
　問題を見る限り、Nを5進数にして、1だったら２、という感じで
　変換すればよさそうな気がしましたが、その解き方だと
　どうにも合わない・・・。
　その後いろいろ調べ対象数値が0から始まり、0を1個目と数えるために
　合わなくなることに気づい、その点修正したところACを頂けました。


　https://atcoder.jp/contests/abc336/submissions/49302142


# 4.D - Pyramid
　ぱっと見分からず、愚直に進むには時間足りなそうだなと迷っていたら
　累積和的な考え方がいけそうと思いいたりました。

　【考え方】
 　　1.ピラミッドの左側からチェックしていき、現在調べている
   　  箇所が、ピラミッドのどの高さの頂点までなれるかをリストに保持
 　　2.ピラミッドの右側からチェックしていき、現在調べている
   　  箇所が、ピラミッドのどの高さの頂点までなれるかをリストに保持
 　　3.2.のリストをリバースし、各頂点につき、左右からどの高さまで
   　　ピラミッドとなれるかを判断。
     　（１と２のリストを比べ小さい方が、ピラミッドになれる高さ）
 　　4.各頂点毎のピラミッドになれる高さのうち最大なものを
   　　答えとして表示して終了。
　
 　せっかく3のリストをリバースすることまで組み込んだのにリバースしてない表を
　　使っていたため、WAが出てしまい時間を無駄にしました。
 　
　https://atcoder.jp/contests/abc336/submissions/49308327


以上
GoogleのOAuth2.0のリフレッシュトークンに関するメモ

GoogleアカウントでOAuth2.0/OpenID Connectの実験をした際ののメモです。

GoogleのOAuth2.0認可エンドポイントにアクセスするとアクセストークンが払い出されます。アクセストークンの有効期限は1時間です。

アクセストークンが期限切れになった場合に再度認可エンドポイントにアクセスしても問題ありませんが、リフレッシュトークンを利用して暗黙にアクセストークンを再発行することもできます。

一度発行されたリフレッシュトークンは無期限で使えます。６ヶ月使われていなければ無効化されますが、利用されている限り有効となります。

# リフレッシュトークンを発行するためのパラメータ指定がある

Googleでリフレッシュトークンを取得する場合、認可エンドポイントへのアクセスの仕方が少し変わります。

下記URLによれば、`access_type=offline`を付けないとリフレッシュトークンが発行されないようです。また、理屈はわかっていないんですが`prompt=consent`も付ける必要があるようです[^1]。

[^1]:https://github.com/pomerium/pomerium/pull/81 を読むとワークアラウンドとか書いてありますね…

https://developers.google.com/identity/protocols/oauth2/web-server?hl=ja

`prompt=consent`の代わりに`approval_prompt=force`というパラメータをつけろという話もググると見つかるんですが、多分これは昔の仕様で廃止予定なんじゃないでしょうか（今も使えるようですが）。

# リフレッシュトークン無効化はすぐ反映される

下記URLによれば、ユーザーがアプリのアクセス権を取り消すことでリフレッシュトークンを無効化できると書いてあります。

https://developers.google.com/identity/protocols/oauth2?hl=ja

実際、アクセス権を取り消すとすぐリフレッシュトークンが無効化されて、下記のようなレスポンスが帰ってきました。

```json
{ error: 'invalid_grant', error_description: 'Token has been expired or revoked.' }
```
ブロックについて

# はじめに


#### <font color="red">※以下の内容には誤りが含まれる可能性があります</font>



# ブロックとは何か



ブロックとはイテレーターを実装したものである。イテレーターとはオブジェクトの要素や値を
１つずつ取り出して処理することです。


# ブロックの使い方

```
3.times do
puts "Hello Ruby!"
end
```

3というリテラルは整数クラスのオブジェクト(正確にはFixnumクラスのオブジェクト)として扱われるので、timesメソッドの後のdo~endの間に書かれている文が3回実行される。


その他、do~endの代わりに{ ~ }を使ってもいい。
```
$ ruby sample.rb
Hello Ruby!
Hello Ruby!
Hello Ruby!
```
プログラムを実行すると、timesメソッドの働きにより、Hello Ruby!が３回表示される。


この段階では、timesメソッドによる繰り返し処理を行っただけで、ブロックの機能を全て使っているわけではない。
Integerオブジェクトの値が0以下の場合、timesメソッドによる繰り返しは実行されない。


# まとめ


- ブロックとはイテレーターを実装したもの。
- ブロックの使い方
```
$ ruby sample.rb
Hello Ruby!
Hello Ruby!
Hello Ruby!
```
[PowerPoint] 図形を保存せずに画像として利用する方法

## はじめに
パワポで複雑な図形を作り、画像として保存するとき、いちいち「図として保存」してませんか？図として保存が必要な場面もありますが、できるだけフォルダの中身を増やしたくない。そんな時に使えるのがこれから紹介する手法です。
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984024/1d93fa31-997d-df7a-8bc3-a2bf345b3554.png">

## 図形を保存せずに画像として利用する
1. 図形を選択して切り取る
2. 切り取った図形をその場で貼り付ける
3. 右下のマークをクリック
4. 「図としてペースト」を選択
<img width=300 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984024/f59987ff-b19e-3c7a-e30d-876733b34d3f.png">

たったこれだけで画像になります。
あとはこれをコピーしてwordやfigmaなどの貼り付け先に貼り付けるだけです。

## 終わりに
すごく便利な機能なのでぜひ使ってみてください。
パッケージ追加・更新時に周知めんどいからSlack通知して自動化したった

## はじめに
この記事では、GitHub Actionsを使って、Dockerのイメージなどの変更やパッケージ管理ファイルの変更などがmainブランチにマージされたときに、他の開発者に`npm install`お願いします〜！みたいな周知を毎回するのめんどくなったので、それを自動的にSlack通知して周知を自動化する方法を紹介します。
これにより、周知忘れによるあれっ？ここなぜか動かないんですが、、、わかりますか？みたいなこともなくそうと思います！
めっちゃシンプルに書いていきます。

## 実施内容
GitHub ActionsとSlackのWebhookを使用して、以下のような例のファイルの変更コミットをmainのブランチにマージされたときに検知し、`npm install`お願い〜みたいなメッセージをSlackチャンネルに通知して周知するようにします。

- composer.jsonとcomposer.lockの変更
    - composer installお願いします〜を自動化できます。
- package.jsonとpackage-lock.json
    - npm installお願いします〜みたいなの自動化できます。
- docker系ファイル
    - ビルドしなおしてください〜みたいな周知を自動化できます。
- データベースのマイグレーションとシーダーファイル
    - migrate(seed)しなおしてください〜を自動化できます。

## 準備
この設定を行うには、以下のものが必要です。

- GitHubリポジトリ
- Slackワークスペース
- SlackのWebhook URL
    - こちら準備する手順は後述

## 実装手順

### SlackのWebhook URLを取得する

1. Slackアプリで、通知を送信したいチャンネルを開きます。
2. チャンネル名の右側にある矢印アイコンをクリックします。
3. 「インテグレーション」のセクションで「アプリを追加する」をクリックします。
4. 「アプリを検索」ボックスに「Incoming WebHooks」を入力し、結果から選択します。
5. 「Add to Slack」ボタンをクリックします。
6. 「Post to」ドロップダウンメニューから通知を送信したいチャンネルを選択します。
7. 「Add Incoming WebHooks integration」ボタンをクリックします。
※Webhook URLをコピーしておきます。


### GitHubリポジトリの設定を開く

1. Secretsタブを開きます。
2. New repository secretボタンをクリックします。
3. Nameに任意の名前を入力し、Valueに先ほどコピーしたWebhook URLを貼り付けます。

### GitHub Actionsのワークフローファイルを作成する

1. .github/workflowsディレクトリに新しいYAMLファイルを作成します。
2. 以下の例のようにワークフローファイルを設定します。
    - これをcomposerバージョンとかDockerバージョンで複数ファイルももちろん作成することもできる

```.github/workflows/◯◯.yml
name: "Notify npm package change"

on:
  push:
    paths:
      - "front/package.json"
      - "front/package-lock.json"
    branches:
      - main

jobs:
  notify_slack:
    runs-on: ubuntu-latest
    steps:
      - name: Notify Slack
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL_EXEC_CMD }}
          SLACK_COLOR: "warning"
          SLACK_MESSAGE: |
            *npmパッケージの変更が検知されました*
            最新のmainブランチにて以下のコマンドの実行をお願いします:
            ```
            docker-compose exec ◯◯ npm install
            ```
          SLACK_USERNAME: Github Actions
          SLACK_ICON_EMOJI: ":bell:"

```

## 結果
これで、今回の説明だとフロントのパッケージ管理ファイルなどが変更されたときに、自動的にSlackに通知されるようになりますので、適当にテストで確認してみてください〜！
これにより、他の開発者が最新の変更を迅速に把握することができるかつ、周知漏れによる時間ロスなどもなくすることができるかと思います。

## 終わりに
GitHub ActionsとSlackのWebhookを使って、パッケージ管理やDockerなどのファイルの変更を自動的に通知することで、いちいち面倒な周知を永久に自動化できます！

## 参考
https://docs.github.com/en/actions

https://api.slack.com/messaging/webhooks
インデックス断片化・欠損調査

インデックスの断片化具合を調査するSQLです。
断片化されたインデックス再構築する場合は、SQLCommand列に出力されるAlter文を実行します。

インデックス断片化調査
``` sql
SELECT
 SCM.name 
,OBJCT.name 
,avg_fragmentation_in_percent
,IDX.name
'ALTER INDEX ' + '[' + IDX.name + ']' + ' ON [' + SCM.name + '].[' + OBJCT.name + '] REORGANIZE' AS SQLCommand
FROM sys.dm_db_index_physical_stats (DB_ID(),null,null,null,null) AS IPS
JOIN sys.objects AS OBJCT ON  IPS  .object_id = OBJCT.object_id  and OBJCT.type = 'U'
JOIN sys.schemas AS SCM   ON  OBJCT.schema_id = SCM.schema_id 
JOIN sys.indexes AS IDX   ON  IPS.object_id   = IDX.object_id  AND IPS.index_id = IDX.index_id and IDX.index_id > 0 
WHERE    IPS.avg_fragmentation_in_percent >= 30
ORDER BY IPS.avg_fragmentation_in_percent DESC
```


インデックス欠損行があるテーブルを調査
``` sql
SELECT mid.object_id,statement AS table_name  
FROM sys.dm_db_missing_index_details AS mid  
CROSS APPLY sys.dm_db_missing_index_columns (mid.index_handle)  
INNER JOIN sys.dm_db_missing_index_groups AS mig ON mig.index_handle = mid.index_handle  
group by  mid.object_id,statement
```
AmplifyでサクッとTODOを作ろうと思ったのだがメインバージョンで色々とハマったこと備忘録（その１）

## とりあえずチュートリアルからやってみる
とりあえずチュートリアルからやってみようとAmplify Dev CenterのDocを漁るところからスタート

Set up fullstack project：

https://docs.amplify.aws/react/start/getting-started/setup/

今回はReactで構築したいのでReactのチュートリアルをとりあえず順にこなしていく

何事もなくauthまでチュートリアルの通りには進められて動くものが出来上がった
しかしここからが問題だった。カスタマイズしていこうとしたところで諸々のメインバージョンのことをすっぽかしていたことに気づいたのだ。
今日時点でバージョン指定せずにチュートリアルをこなすと下記となる。
<table>
  <tr>
    <td> react </td> <td>18.2.43</td>
  </tr>
  <tr>
    <td> aws-amplify </td> <td>6.0.12</td>
  </tr>
</table>

reactのメインバージョンが１８なのは認識済みだったのだがaws-amplifyのバージョンが6だったことが今回ハマった要因だった。
まずAuthのimportの書き方から5と６では異なる
```
import { Auth } from 'aws-amplify';
```
これがAttempted import error: 'Auth' is not exported from 'aws-amplify' (imported as 'Auth').となるのだ。
これについてはIssueなどを検索して6.x.xの最新バージョン ( ) で、Auth クラスが利用できなくなるなど、エクスポートが変更されたためだとわかった。

参考：

https://github.com/aws-amplify/amplify-js/issues/12631

その他react-router-domも使おうと最新の6.21.2をimport。こちらもまたメインバージョンが6.x.xから変更が加わっているため調査からスタートする羽目に。

## その１のまとめ
チュートリアルをこなして動くものができるのは理解したとは全くの別物である。
次回は実際にカスタマイズした実装を書きながら出来上がったものの全体像を備忘録として書こうと思う。
twitchの配信時系列を作成・閲覧可能なアプリケーションを作成しました．

![app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/c2d99b78-075c-2641-6ff6-cc5d4651d61c.png)

## はじめに
Twitchの配信で長期的な大会やイベントを見ている時に，同じチームや対戦相手について，どんな配信をしていたか，どんな面白いクリップがあるのかといったことが一目で確認できればいいなと考えて，このアプリケーションを開発しました．
## 成果物
- リポジトリ

https://github.com/H-Tsukasa/nf-twitch

- ~~公開URL~~
    - 現在vercelのエラーに対応中
## 使用技術
- バックエンド
    - `python`
    -  `FastAPI` 
        -  `Flask`と比較して，型の認証やswaggerを容易に表示可能などAPIに特化しており，今回はAPIのみの利用であるためこちらを選択．
        - `Firebase`のJWT認証
        - データベースの操作，モデルの作成，APIの作成 
    - `sqlalchemy`
- フロントエンド
    - `Next.js`, `tailwind`,`Typescript`
        - 今回は`Next.js`と`Typescript`の勉強も兼ねて選択
    - `shadcn-ui`
        - モーダル，ドロップダウンメニューの作成に利用
- 認証
    - `FireBase`
        - Googleアカウントユーザの管理，ログイン処理
        - JWTトークンによる認証は`python`を利用
- データベース
    - `PlanetScale`
        - ストリーマーの情報（ビデオ，クリップなど）を保存
        - 時系列データの保存
- ~~サーバ~~
    - ~~vercel~~
## 実装・機能
### Twitch APIによるデータ取得
- Twtich APIでは，ユーザ，クリップ，ビデオなどのデータが提供されています．ストリーマーとそれに関連するビデオなどの情報が欲しかったため，[公式リファレンス](https://dev.twitch.tv/docs/api/)を参考にデータを取得しました．
- 今回は事前に決めたストリーマー100名のデータを呼び出し，取得したユーザIDからビデオ，クリップのデータを呼び出しました．
### データの更新
- ビデオやクリップは，新たに追加され，再生数等も変化するため定期的な更新が必要です．そのため，一日に一度アクセストークンを取得し，登録されているストリーマーのビデオとクリップのデータを取得し，データベースに登録しています．
- なお，ビデオとクリップには一度に呼び出し可能な個数が100個までという制限があります．APIの呼び出し回数を考慮し，今回は直近100件のデータのみを更新対象としています．
### アプリケーションの操作手順
- 時系列の作成
ログイン後，以下の画面が表示されます．このページのサイドバーか本ページにあるプラスボタンをクリックすることで，時系列作成モーダルが表示されます．
![register1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/035570ba-6fbd-b3f5-e8de-1ada9c5c7967.png)
時系列の名前とその日付の範囲を選択することで，時系列を作成できます．
![register2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/d4fb8575-7fcf-8db1-2a7b-86cf49aaa35a.png)
- ストリーマーの追加
時系列作成後，以下の様な画面が表示されます．時系列の名前をクリックすると，時系列に関する操作を行えるので，ストリーマーの追加を選択します．
![addstreamer1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/932400af-716a-afef-8e0e-cfc042afe6e0.png)
ストリーマーの一覧が表示されるので，いずれかのストリーマーを選択します．
![addstreamer2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/7c3015e4-7ed3-e48c-a136-6c5f6035de09.png)
追加を行うと選択した範囲内におけるビデオの時系列データが作成されます．
![result2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/02f95282-fbf5-8cc7-9cb6-af0404afab44.png)
また，時系列上のデータをクリックすると，そのデータに応じたビデオが表示されます．
![selectclip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/236c2f5f-00cf-9f00-2b9a-cb141e2740c7.png)
上のラジオボタンを押すことで，クリップの時系列を表示することも可能です．細かい機能についてはビデオと同様です．
![clip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671399/d1ce2bfd-8ed1-2458-b6ae-bf224757ee83.png)

## 今後実装予定・実装の課題
### ストリーマー検索機能
- 現在は事前に選んだストリーマーのみが利用可能です．新たにストリーマーを追加できるように，APIを発行し検索する機能を実装予定です．
### 時系列検索機能
- 他者の作成した時系列を検索，評価できる機能を実装予定です．
### Twitch APIの制限
- Twitch APIではビデオにゲーム情報が無い，ビデオとクリップの一度取得個数に限度があるなどの制限が存在します．
- ビデオに関してゲーム情報を取得する方法を現在模索しています．
### `FastAPI`と`Next.js`の連携
- `FastAPI`のAPIによってデータの登録を行う際，`Next.js`側でリロードを行わないと登録されたデータが表示されないなど，連携の上での知識がまだまだ不足しています．これらの問題について調査していく必要があります．
【SwiftUI】Chartの棒グラフでグラデーションを設定した際にグラデーションの終点を揃える

# はじめに
SwiftUIのChartを極めようの会を1人でしてます。

# 解決したい課題
棒グラフにグラデーションを設定します。
```swift
import SwiftUI
import Charts

struct Temperature {
    let day: Int
    let max: Double
    let min: Double
}

struct ContentView: View {
    @State private var temperatures: [Temperature] = [
        .init(day: 1, max: 17, min: 13),
        .init(day: 2, max: 25, min: 10),
        .init(day: 3, max: 12, min: 11),
        .init(day: 4, max: 21, min: 11),
        .init(day: 5, max: 10, min: 6)
    ]
    
    var body: some View {
        Chart {
            ForEach(temperatures, id: \.day) { temperature in
                BarMark(
                    x: .value("day", temperature.day),
                    y: .value("max", temperature.max),
                    width: 30
                )
                .foregroundStyle(by: .value("max", "最高気温"))
            }
        }
        .chartForegroundStyleScale(["最高気温": LinearGradient(colors: [.purple, .red, .orange, .yellow], startPoint: .top, endPoint: .bottom)])
        .frame(width: 300, height: 300)
    }
}
```

上記のコードで実行すると以下のようなUIになります。

![スクリーンショット 2024-01-14 21.45.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/ef7889b7-bb14-a48c-cf6b-ab78ddc4c118.png)

まぁ悪くないですが、グラデーションの終点が揃っていなので色から高い低いが分かりにくいです。

以下のようにグラデーションの始点と終点を揃えることによって、色からも高い低いが理解しやすいです。

![スクリーンショット 2024-01-14 21.47.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f6a32c72-e31c-3618-9bd1-7d1701b8e114.png)

ではSwiftUIのChartでどのように実装するのでしょうか？

# 実装
```diff_swift
import SwiftUI
import Charts

struct Temperature {
    let day: Int
    let max: Double
    let min: Double
}

struct ContentView: View {
    @State private var temperatures: [Temperature] = [
        .init(day: 1, max: 17, min: 13),
        .init(day: 2, max: 25, min: 10),
        .init(day: 3, max: 12, min: 11),
        .init(day: 4, max: 21, min: 11),
        .init(day: 5, max: 10, min: 6)
    ]
    
    var body: some View {
        Chart {
            ForEach(temperatures, id: \.day) { temperature in
                BarMark(
                    x: .value("day", temperature.day),
                    y: .value("max", temperature.max),
                    width: 30
                )
                .foregroundStyle(by: .value("max", "最高気温"))
+               .alignsMarkStylesWithPlotArea()
            }
        }
        .chartForegroundStyleScale(["最高気温": LinearGradient(colors: [.purple, .red, .orange, .yellow], startPoint: .top, endPoint: .bottom)])
        .frame(width: 300, height: 300)
    }
}
```

# おわり
`alignsMarkStylesWithPlotArea`を付与することによってグラデーションの終点が揃えられました。

# 公式ドキュメント
https://developer.apple.com/documentation/charts/chartcontent/alignsmarkstyleswithplotarea(_:)
Salesforceの活動について解説してみた（Todo、行動、活動予定、活動履歴、カレンダー）

こんにちは
本橋孝昭です

今回はSalesforceの活動について解説します
Salesforceの活動はややこしい部分が多いので、そこを含めて解説します

今回の内容は動画でも公開しています

https://youtu.be/bAG7eTH7fNc?si=ezQq27wUVEuKoeRe

<iframe width="560" height="315" src="https://www.youtube.com/embed/bAG7eTH7fNc?si=ezQq27wUVEuKoeRe" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>


## Salesforceの活動とは？

Salesforceの活動とは電話や訪問などの業務記録を管理するためのオブジェクトです

詳細はこちらのサクセスナビをご確認ください

https://successjp.salesforce.com/article/NAI-000032#:~:text=%E6%B4%BB%E5%8B%95%E3%81%A8%E3%81%AF%E3%80%81%E9%9B%BB%E8%A9%B1%E3%82%84,%E9%AB%98%E3%82%81%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82


活動オブジェクトですが、実際に活動できるレコードデータには「活動」オブジェクトのデータはなく、「ToDo」か「行動」になります
なお、「ToDo」とは馴染みがあまりない単語かと思いますがこれは日本語名であり、通常（英語名）では「Task」オブジェクトとなります。

それではToDoと行動について見に行きましょう

## ToDoと行動と活動について

ToDoと行動はそれぞれ項目を共通として持っております
また、ToDoオブジェクト、行動オブジェクトにはカスタム項目を作成することができないです
ToDoオブジェクト、行動オブジェクトでカスタム項目を作成して表示したい場合、（ToDoや行動ではなく）「活動」でカスタム項目を作成しそれぞれに表示させることになります

## ToDoの使い方
ToDoの使い方は主に「タスクの管理」に使います
・〇月〇日までに山田さんに電話する
・〇月〇日までに鈴木さんに書類提出をする

など、「ここまでに完了させる」タスクの管理にToDoは向いています


## 行動の使い方
行動の使い方は主に「特定の日時におけるスケジュール管理」に使います
・〇月〇日〇時から〇時に山田さんに電話した（電話する）
・〇月〇日〇時から〇時に山田さんに訪問した（訪問する）

など、「特定の日時に起こる」スケジュールの管理に行動は向いています

## 活動予定、活動履歴の考え方

ToDo、行動の活動は「活動予定」「活動履歴」という考えを持ちます
ToDoの場合、ToDoの状況を「完了」に設定してないものが「活動予定」のToDo、
ToDoの状況を「完了」に設定したものが「活動履歴」のToDoです
なお、「完了」に設定するのは基本は手動でありますので、ToDoは完了後に手動でデータ更新することが前提となっております


行動の場合、スケジュール日時が過ぎた場合に自動で「活動予定」から「活動履歴」になります
例として、今が2024年1月1日の場合、2024年1月10日の訪問予定の行動があれば、それは未来の話なので「活動予定」の行動です
しかし、2024年1月11日になって際、先ほどの2024年1月10日の行動は過去の話になるので「活動履歴」の行動になります
このように、行動の場合、「活動予定」の行動は自動的に「活動履歴」になります

## 行動おまけ
行動で他のオブジェクトとは異なることは「行動」タブがデフォルトにはなく「カレンダー」タブから行動の確認をすることです
（※「ToDo」は「ToDo」タブがあるので他のオブジェクトと同様にリストビューでの閲覧が可能です）


## 最後に
今回ご説明した内容を一覧にまとめました


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/de83e505-f3da-6733-8ffc-949636275e22.png)
AI開発における倫理的考察

人工知能（AI）が進歩し、私たちの生活の様々な分野に統合され続けるにつれて、その倫理的な意味合いをめぐる議論はますます複雑化し、必要性が高まっている。本記事では、<a href="https://xn--gpt-1l4bk4a7o.co/">チャットgpt</a> やBARDをはじめとする多くのAPIツールのようなAI開発における倫理的配慮について掘り下げる。クリエイターの責任、社会への影響、そして倫理的AIの未来を探る。

<b>倫理的基盤に基づいた責任あるAI環境の構築<b>

AIの開発は単なる技術的な旅ではなく、道徳的な旅でもある。それは、テクノロジーが人類の利益を向上させるだけでなく、私たちの価値観や規範を尊重するような基盤を構築することである。AIにおける倫理的配慮は、公平性、透明性、説明責任、人間の幸福への影響といった問題を中心に展開される。

<b>AIにおける平等のためにバイアスのないテクノロジーを目指して<b>

AI開発における重要な倫理的懸念は、バイアスの問題である。AIシステムはデータから学習するが、このデータに偏りがあれば、AIの判断にも偏りが生じる可能性が高い。これは、採用、法執行、ローン承認などの分野で不公平な結果を招く可能性がある。公正さを追求するには、データの多様性と包括性を確保し、偏った結果になっていないか常に監視する必要がある。

![DALL·E 2024-01-14 17.29.06 - A symbolic illustration representing the ethical considerations in AI development. The image features a large, human-like robotic hand gently holding .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641534/50884890-0b51-95fa-f400-6935ab4bed1b.png)


公開性と説明責任のためにAIの意思決定プロセスを理解する
AI、特にディープラーニングのような複雑なシステムでは、ブラックボックス化する可能性があり、どのようにして特定の意思決定に至るのかを理解するのは難しい。この透明性の欠如は、特に医療や刑事司法のような重要なアプリケーションにおいて問題となる可能性がある。AIシステムの透明性を確保し、説明責任を果たす仕組みを構築することは、信頼と信用を築く上で極めて重要である。

AIが人類に与える影響 雇用
AIをめぐる倫理的問題として最も議論されているのが、雇用への影響である。AIシステムの能力が高まるにつれ、雇用の場が奪われることへの懸念が高まっている。AI開発に対する倫理的なアプローチは、労働力への潜在的な影響を考慮し、再教育プログラムや教育などを通じて悪影響を軽減する方法を探るべきである。

<b>AI時代の個人情報セキュリティ<b>

AIは膨大な量のデータを処理できるため、プライバシーに関する懸念は最も重要である。倫理的なAI開発には、個人データを保護するための厳格な対策を盛り込み、監視やその他の侵略的な目的に悪用されないようにしなければならない。これには、どのようなデータが収集され、それがどのように使用されているかについて、ユーザーに対して透明性を確保することも含まれる。

AI倫理の広範な影響に関する国際的見解
AI倫理は単なるローカルな問題ではなく、グローバルな問題である。文化や国が異なれば、AIにおいて何が倫理的と見なされるかという観点も異なる。こうした多様な視点を尊重するグローバルな基準や枠組みを策定することは、AIが世界的に普及する中で取り組むべき課題である。

進むべき道 倫理的AIの未来を拓く
AIの未来は、技術的な進歩によって形作られるだけでなく、私たちがどのように倫理的な配慮に取り組むかによっても左右される。技術者、倫理学者、政策立案者、そして一般市民の間で継続的な研究、対話、協力が行われることが、すべての人にとって有益で尊重される形でAIが発展していくためには不可欠である。2024年の日本のAIツールの驚くべき例は、<a href="http://chatgptjapanese.org/">ChatGPT日本語</a> 版だ。

<b>最後に AI開発のための道徳的指針<b>

AI開発における倫理的配慮は、我々が創造するテクノロジーが我々の道徳的価値観に合致し、より大きな利益に貢献することを保証するために極めて重要である。AIの大きな可能性を探求し続ける中で、こうした倫理的配慮を最重要視することは、テクノロジーと人類が調和して共存する未来のために不可欠である。

主な考慮事項には、公平性の確保と偏見の回避、透明性と説明責任の維持、雇用への影響への対処、プライバシーの保護などがある。

なぜAIにおいて公平性が重要なのか？
AIが社会的偏見を永続させたり増幅させたりするのを防ぎ、AIシステムがすべての個人や集団を公平に扱うようにするためには、公平性が極めて重要です。

AIシステムにおいて透明性はどのように保たれるのか？
透明性は、AIの意思決定プロセスをより理解しやすくし、ユーザーがアクセスできるようにし、AIの決定に対する明確な説明責任を確保することによって達成することができる。

AIと雇用に関する懸念は？
主な懸念は、AIの自動化によって雇用が奪われ、特定の分野で失業や不完全雇用につながる可能性があることだ。倫理的なAI開発は、こうした影響を考慮し、再教育プログラムのような解決策を探るべきです。

AIの開発をグローバルに倫理的なものにするにはどうすればよいか？
AIのグローバルな倫理基準を策定し、多様な文化的・社会的価値観を考慮し、AI倫理の研究と政策立案における国際的な協力を奨励することが、世界中で倫理的なAI開発を行うための鍵となります。
Java csv 値挿入

~~~java
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

public class CSVInsertValue {

    public static void insertValueToCSV(String inputFilePath, String outputFilePath, int columnIndex, String newValue) {
        try (CSVReader reader = new CSVReader(new FileReader(inputFilePath));
             CSVWriter writer = new CSVWriter(new FileWriter(outputFilePath))) {

            // CSVファイルの読み込み
            List<String[]> rows = reader.readAll();

            // 特定の列に値を挿入
            for (String[] row : rows) {
                String[] newRow = new String[row.length + 1];
                // 挿入位置より前の要素をコピー
                System.arraycopy(row, 0, newRow, 0, columnIndex);
                //値を追加
                newRow[columnIndex] = newValue;
                // 挿入位置より後の要素をコピー
                System.arraycopy(row, columnIndex, newRow, columnIndex + 1, row.length - columnIndex);
                writer.writeNext(newRow);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String inputFilePath = "input.csv";
        String outputFilePath = "output.csv";
        int columnIndex = 2;  // 挿入する列のインデックス（0から始まる）
        String newValue = "新しい値";

        insertValueToCSV(inputFilePath, outputFilePath, columnIndex, newValue);
    }
}
~~~

~~~java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class CSVInsertValue {

    public static void insertValueToCSV(String inputFilePath, String outputFilePath, int columnIndex, String newValue) {
        try (BufferedReader reader = new BufferedReader(new FileReader(inputFilePath));
             BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath))) {

            String line;
            while ((line = reader.readLine()) != null) {
                List<String> values = new ArrayList<>();
                String[] columns = line.split(",");
                
                // 特定の列に値を挿入
                for (int i = 0; i < columns.length; i++) {
                    if (i == columnIndex) {
                        values.add(newValue);
                    }
                    values.add(columns[i]);
                }

                // 新しい行を書き込む
                writer.write(String.join(",", values));
                writer.newLine();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String inputFilePath = "input.csv";
        String outputFilePath = "output.csv";
        int columnIndex = 2;  // 挿入する列のインデックス（0から始まる）
        String newValue = "新しい値";

        insertValueToCSV(inputFilePath, outputFilePath, columnIndex, newValue);
    }
}

~~~
Raspberry Pi 4Bでヘッドセット(マイク,スピーカー)の動作確認

# 1. はじめに
　Raspberry Piで双方向通話ができないか試している。当然、スピーカーやマイクが必要になるが、Raspberry Piで音を扱ったことがほとんどない。そこで、いくつかの手持ちの機器でサウンドの操作について調べてみることにした。

 # 2. 機材
 * Raspberry Pi 4B
OSは、Raspberry OS(Bullseys Lite 64bit) 2023/05/03版 
 * ヘッドセット
ELECOM HS-HP07SUBK
https://www.elecom.co.jp/products/HS-HP07SUBK.html

 マイク付きヘッドセットが色々と便利そうで楽そう。

# 3. 準備
ヘッドセットは、USB 2.0の下側（LANが右にある状態でみている）に挿した。
音源が必要なので、ネットで適当にフリーのWAV音源を探す。探したらwgetで取得しておく。以後、test.wavとしておく。
```
wget https:/＜検索してみつけたURL＞/ファイル名.wav
```


USBデバイスとしての認識を確認。
2行目の「USB PnP Audio Device」がそれっぽい
```
pi@raspberrypi:~ $ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 006: ID 0c76:161f JMTek, LLC. USB PnP Audio Device
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```

サウンドモジュールの確認
デフォルトからUSB Audioが増えている。
```
pi@raspberrypi:~ $ cat /proc/asound/modules
 0 snd_bcm2835
 1 vc4
 2 vc4
 3 snd_usb_audio
```

再生デバイスのリストを表示。
カード3として認識されている。
```
pi@raspberrypi:~ $ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 1: vc4hdmi0 [vc4-hdmi-0], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: vc4hdmi1 [vc4-hdmi-1], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 3: Device [USB PnP Audio Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

# 4. 試す
# 4.1. 再生テスト
ハードウェアの番号で指定する。カード3だったので、3,0とする。
```
aplay --device=hw:3,0 test.wav 
```

番号ではなくてaplay -Lで出てきた。識別で指定もできる。
```
xacti@raspberrypi:~ $ aplay -L
～略～
hw:CARD=Device,DEV=0
    USB PnP Audio Device, USB Audio
    Direct hardware device without any conversions
plughw:CARD=Device,DEV=0
    USB PnP Audio Device, USB Audio
    Hardware device with all software conversions
sysdefault:CARD=Device
    USB PnP Audio Device, USB Audio
    Default Audio Device
～略～
```

```
aplay --device=plughw:CARD=Device,DEV=0 test.wav
```
でも鳴るし、
```
aplay --device=hw:CARD=Device,DEV=0 test.wav
```
でも鳴った。


# 4.2. 録音テスト
こちらもハードウェアとして3番を指定する。
マイクに向かって適当に話す。
```
arecord -D plughw:3 -f cd rectest.wav
```

再生すると自分で話したことが聞こえてきた。
```
aplay --device=hw:3,0 rectest.wav 
```

# 5. 今後
* その他のヘッドセット、マイク、スピーカーでテスト
* 実際の通信で使ってみる。

以上
【AWS】Lambda関数のレイヤー作成と利用【Lambda】

## はじめに
AWS Lambda Layers(レイヤー)を使用すると複数のLambda関数で共通のコードやライブラリを共有できます。これによりコードの再利用性を向上させ、Lambda関数間で共通の機能やライブラリを簡単に管理できます。
今回の記事ではこのAWSLambdaLayerの作成、設定方法、レイヤーを使うメリットについて記載しています。

## レイヤーの作成/設定手順(ローカル環境で作成する場合)
### 1. Lambda Layer用のプログラムの作成:
#### ①Pythonコードの作成
・ここでは文字列を返すだけのコードを実装しています
![2_Code作成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/8fa74653-7d1e-93f1-029f-4a66d382befc.png)


#### ②依存関係を取得:
・コードが依存している外部パッケージがあれば、これらを取得します。通常、pipを使用して仮想環境で必要なパッケージをインストールします。
・pythonがWindows環境にインストールされていてパスが通っていれば以下のコマンドをコンソール画面で入力すればインストールが可能です
```
pip install -r requirements.txt -t [インストール先パス]
```
(実行例)
pip install -r requirements.txt -t ./layerCode

#### ③Pythonコードのzip化
・Windows環境であれば右クリックからの圧縮を選択してZip化が可能です。

:::note warn
★注意★
Zip化後のファイル名の指定は任意ですが、解凍(展開)後のファイルは「python」になるようにフォルダ名を変更してください。pythonではない場合Lambda関数からレイヤーの処理の呼び出しに失敗してしまいます。
![Zip化するフォルダ名.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/e45441bb-995f-e420-2fba-f180ae3136a9.png)
(フォルダ名が異なるため処理失敗したときの画面キャプチャ)
![フォルダ名誤り時のエラー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/2a5692f7-45b5-ee97-18c4-1860bcaa9750.png)


:::


### 2. AWSコンソールからレイヤーの作成(アップロード):
#### ①Lambdaコンソール画面から「レイヤーの追加」を選択
![6_レイヤー作成1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/23bf9e44-9817-9b19-5225-d36cddf5b443.png)
#### ②レイヤーの各種情報の入力
・アップロード方法はZipファイルの直接アップロードとAmazonS3にファイルをアップロードした後でそのURLを指定する方法があります
※10MB以上のファイルの場合はS3からファイルをアップロードする選択肢になります
・ランタイムはレイヤーを読み込むLambda関数と互換性のあるランタイムを選択してください
![7_レイヤー作成2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/36c98c73-050d-b36b-3068-9223916b4cdd.png)

### 3. Lambda関数へのレイヤーの読み込み
#### ①Lambda関数の設定画面からレイヤーの追加を選択
![8_レイヤー設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/a929e39a-a74b-ccd1-4de8-ded36f59a315.png)
#### ②2の手順で追加したレイヤー(カスタムレイヤー)を選択して追加します
![9_レイヤー設定.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/3f105c8c-d6a4-c089-0be8-2a9f704cae9c.png)


### 4. Lambda関数のコードでレイヤーに含まれる処理の呼び出し
・importでレイヤーを指定します

:::note warn
★注意★
このimportでは「1の手順で作成したpythonファイルの名前」を指定して下さい。レイヤーの名前ではありません
:::
・コード中の必要な場所でレイヤー中の処理を呼び出してください

![10_レイヤーから処理呼び出し.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/4ec504c9-71ca-70f6-a192-db73a5364ade.png)

### 5. 結果確認
・レイヤーのプログラムで実装した文字出力がされていることを確認しました
![10_結果確認.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/7cc992e3-4c82-b57b-7bab-4179c9e2eaa7.png)


## Lambdaレイヤーを使うメリット
## 1．コードの再利用性
レイヤーを使用することで、関数間でコードを共有しやすくなります。特に複数のLambda関数で同じライブラリや共通の機能を使用する場合、それらのコードをレイヤーにまとめておくことで、冗長性を減少させ、コードの再利用性を向上させることができます。

## 2．依存関係の管理
レイヤーは、Lambda関数が依存する外部ライブラリやモジュールを管理するための便利な方法です。関数ごとに同じ依存関係を手動で管理する必要がなくなり、レイヤーを更新するだけで関連するすべての関数が更新されます。

## 3．デプロイの簡素化
レイヤーを使用することで、Lambda関数のデプロイプロセスが簡素化されます。関数自体のコードが独立しているため、変更があった場合でも、関数ごとに再デプロイする必要がなく、レイヤーだけを更新することが可能です。

## 4．サイズの削減
・Lambda関数のパッケージングサイズを減らすことができます。共通のコードやライブラリがレイヤーに含まれているため、個々の関数のパッケージングサイズは小さくなり、デプロイや起動の速度が向上します。

## 5．セキュリティと一貫性
・レイヤーを使用することで、セキュリティの向上とコードの一貫性を確保できます。共通のセキュリティ対策や認証メカニズムをレイヤーに統合することで、関数ごとに同じセキュリティポリシーを確実に適用できます。
Clean Architecture超概要(前半)

[次の記事へ](https://qiita.com/sakaki_developer/items/060e7c777ade8f45fddf
)

# はじめに
クリーンアーキテクチャについての理解をまとめようと思います。誤っている部分やディスカッションなどあればコメント頂けたら嬉しいです。

### 記事の構成
  

- Clean Architecture超概要編(前半)
章構成: 要約 = 手段 + 効果
**今ここ👆**
- Clean Architecture概要編(中半)
章構成: 手段の説明
- Clean Architectureソース解説編(後半)


# Clean Architectureの要約
クリーンアーキテクチャはソフトウェアアーキテクチャの設計パターンの一つで、、Robert C. Martin（通称ボブ・マーチン）により提唱されました。ソフトウェアの設計と構造を整理し、保守性や拡張性を向上させることを目指します。

具体的には、①各レイヤー（コンポーネント）の独立性を確保し、②依存関係の方向性を定義することで、ソースコードへの変更がシステム全体に及ぼす影響を最小限に抑え、テストの容易さを実現します。


## 実現のステップ（手段）
### 1. レイヤーを独立させる
[各レイヤー（コンポーネント）が独立]()しており、それぞれが[単一の責任](https://www.ogis-ri.co.jp/otc/hiroba/others/OOcolumn/single-responsibility-principle.html)を持つことで、システムが柔軟かつ拡張可能になります。
またレイヤー内のクラスにおいても単一の責務にする必要がある（単一責務の原則）

一般的にはドメイン層がインフラ層に依存しているため、ドメイン層のビジネスロジックの変更がインフラ層に影響が出てしまう。


### 2. 依存の方向性を決定(依存性逆転の原則)
依存性のルールが定義し、外部の変更が内部のビジネスロジックに影響を与えにくい構造にします。
通常は、内部ロジックは外部システムやDB, UI（変更が多い層）に依存しますが、その逆で内部ロジック（変更が少ない層）に依存するようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2171762/42e49bf9-455b-4d22-77f1-c7c33ef0fa5f.png)



## 効果
ソフトウェアを複数のレイヤーに分けることで関心の分離を達成します。これにより、ビジネスルール専用のレイヤーとインターフェイス専用のレイヤーが確立されます。

これらのアーキテクチャアプローチは、以下のような特徴を持つシステムを生み出します：
- 変更への柔軟性とメンテナンスの容易さ。
- 明確な責任分担によるコードの理解性と再利用性の向上。
- テストしやすい構造による品質の維持と向上。

https://qiita.com/kichion/items/aca19765cb16e7e65946

[次の記事へ](https://qiita.com/sakaki_developer/items/060e7c777ade8f45fddf
)

# 参考
| https://gist.github.com/mpppk/609d592f25cab9312654b39f1b357c60
RTXなGPUの載ったゲーミングノートPCにubuntu22.04LTSとNVIDIAドライバをインストールするまで

# 必要なもの

- 別のubuntu PC (またはWindowsPC)
- USBメモリ

# ブータブルUSBの作成

ubuntuに標準で入っているブータブルUSBの作成を使用する。

## isoファイルのダウンロード

こちらのサイトのどれかから`ubuntu-ja-22.04-desktop-amd64.iso`ファイルをダウンロードする。

https://www.ubuntulinux.jp/News/ubuntu2204-ja-remix

## ブータブルUSBの作成

- `ブータブルUSBの作成`アプリを起動
- 先程ダウンロードしたisoファイルとUSBのディスクを選択して`ブータブルUSBの作成`をクリック


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399606/503c691e-b3fd-3af3-c169-4e9c79af3fd3.png)



Windowsの場合Rufusなどを使用する。

https://naberium.com/2023/04/20/%E3%80%90ubuntu%E3%80%91rufus-%E3%81%A7-ubuntu-%E3%81%AE%E3%83%96%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB-usb-%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%BC%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B/


# ubuntu22.04のインストール

こちらの`インストール`にしたがってインストールする。
BIOS・UEFIからUSBを起動する方法やセキュアブートなどについては機種によって異なるのでここでは省略。

https://www.kkaneko.jp/tools/ubuntu/ubuntudesktop.html


# NVIDIA ドライバのインストール


## ディスプレイサーバーをWaylandからXorgに切り替える

https://kledgeb.blogspot.com/2022/05/ubuntu-2204-95-xorgwayland.html

## Nouveau の無効化

```bash
sudo gedit /etc/modprobe.d/blacklist-nouveau.conf
```

nouveauの設定ファイルを新規作成して以下を記入して保存する

```blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
```

保存したら以下を実行

```bash
sudo update-initramfs -u
```


## インストールされているNVIDIAドライバの削除

```bash
sudo apt remove --purge nvidia*
```




## NVIDIAのサイトから.runファイルをダウンロード

ハードウェアに対応したドライバを探す


```
ubuntu-drivers devices
```

```
$ ubuntu-drivers devices

== /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 ==
modalias : pci:v000010DEd000025A0sv00001D05sd00001173bc03sc02i00
vendor   : NVIDIA Corporation
model    : GA107M [GeForce RTX 3050 Ti Mobile]
manual_install: True
driver   : nvidia-driver-535-server - distro non-free
driver   : nvidia-driver-535-server-open - distro non-free
driver   : nvidia-driver-525-server - distro non-free
driver   : nvidia-driver-470 - distro non-free
driver   : nvidia-driver-535 - distro non-free recommended
driver   : nvidia-driver-525 - distro non-free
driver   : nvidia-driver-470-server - distro non-free
driver   : nvidia-driver-535-open - distro non-free
driver   : nvidia-driver-525-open - distro non-free
driver   : xserver-xorg-video-nouveau - distro free builtin

```

recommendedと書いてあるバージョンのNVIDIAドライバのインストーラをNVIDIAのサイトからダウンロードする。

https://www.nvidia.co.jp/Download/index.aspx?lang=jp

実行権限をつける

```bash
sudo chmod +x NVIDIA-Linux-x86_64-525.60.11.run
```


## コンソールモードにする

［Ctrl］＋［Alt］＋［F6］キー を押してコンソールモードに切り替えログインする


## GUIサービスの停止

```bash
sudo service lightdm stop
```

## インストーラを実行して再起動

```bash
 sudo ./NVIDIA-Linux-x86_64-525.60.11.run
 reboot
```

## 動作確認

```bash
nvidia-smi
```












# 参考

https://qiita.com/porizou1/items/74d8264d6381ee2941bd



https://pc.watch.impress.co.jp/docs/column/ubuntu/1544354.html
【Android】GoogleMap上に円や線以外のものを表示する【Kotlin】

# はじめに　
今回は、前回に引き続きGoogleMap上に色々表示できるようにしていこうと思います。
前回の記事は　こちら↓
**[【Android】GoogleMap上に円や線を表示する【Kotlin】](https://qiita.com/Tsubasa12/items/6c2fb5128c5562faca7a)**

# 画像

```
googleMap.addGroundOverlay(
    GroundOverlayOptions()
　　　　　　　　    .position(LatLng(35.689, 139.692), 500f)
        .image(BitmapDescriptorFactory.fromResource(R.drawable.xxxx))  
        .anchor(0f, 1f)
    )
```
または、
```
googleMap.addGroundOverlay(
    GroundOverlayOptions()
        .positionFromBounds(LatLngBounds(LatLng(20.0, 140.0), LatLng(40.0, 145.0)))
        .image(BitmapDescriptorFactory.fromResource(R.drawable.ic_earth))
    )
```

|プロパティ|内容|その他|
|:---|:---|:---|
|Points|中心位置の緯度経度(LatLng)と、画像の幅(m)を指定する、もしくは、バウンディングボックス(LatLngBounds)を指定する。|位置が指定される前にオーバーレイがマップに追加された場合、IllegalArgumentExceptionのエラー、高さを指定しない場合は、画像のアスペクト比によって自動で計算される
|Image|Bitmapで表示|幅、または高さが1024を超える場合、IllegalArgumentExceptionのエラー  指定される前にオーバーレイがマップに追加された場合、IllegalArgumentExceptionのエラー
|Bearing|画像を時計回りに回転する量|デフォルト値は0
|Transparency|オーバーレイの透明性。0が不透明、1が透明|デフォルトは0
|visible|地図上に表示されているかどうか、trueなら表示|デフォルトはtrue
|clickable|オブジェクトをクリックしたときのイベントをハンドリングしたい場合は、このプロパティにtrue|デフォルトはfalse
|zIndex|描画する順番|デフォルトは0


# タイル

```
googleMap.addTileOverlay(
    TileOverlayOptions()
        .tileProvider(
        heatmapTileProviderWithData(latLngs,50,Gradient(colors, startPoints))
         )
     )
```

|プロパティ|内容|その他|
|:---|:---|:---|
|Radius|10 から 50 の間で指定するヒートマップのぼかし|デフォルト値は20
|Gradient|ヒートマップが色。二つの色のListで指定する|
|Opacity|オーバーレイの透明性。0が不透明、1が透明|デフォルトは 0.7

# 参考



https://developers.google.com/maps/documentation/android-sdk/tileoverlay?hl=ja


https://support.e-map.ne.jp/manuals/ref/Android/com/zdc/android/zms/maps/model/GroundOverlay.html