[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[orca-users:15801] Re: 訪問診療と巡回セールスマン問題
- To: ORCA全般の話題 <orca-users@xxxxxxxxxxxxxx>
- Subject: [orca-users:15801] Re: 訪問診療と巡回セールスマン問題
- From: Hiroaki Inomata <inomatah0612@xxxxxxxxx>
- Date: Tue, 1 Apr 2025 03:40:26 +0900
訪問診療系の電子カルテ(ユーティリティソフト?)に手を出す可能性も出てきたので
リハビリも兼ねて、(ブラウザではなく)サーバの方のデータベースに患者用のテーブル、
そのテーブルに通常住所と駐車場住所情報格納用のカラムを作成する。
OpenDolphin 系の場合は、以下のようなコード(Patient.java とでもしておきましょうか)を
追加することで勝手にテーブルとカラムを作ってくれました。
PostgreSQL のデータベースでは、address_home は character_varying(255)、
address_parking_lat などは double prescison になるようです。
dolphin の場合は、hibernate というデータベースを操作するライブラリが組み込まれている
ので、こういうのは簡単なんですよ。
キモは、カラムにしたい変数を private で定義しておいて
> private String address_home;
ゲッターとセッターを追加するだけ。
> public String getAddress_home() {
> return address_home;
> }
>
> public void setAddress_home(String address_home) {
> this.address_home = address_home;
> }
import jakarta.persistence.CascadeType;
import java.io.Serializable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import java.util.List;
@Entity
@Table(name="patient", uniqueConstraints =
{@UniqueConstraint(columnNames={"patientid",
"facility_id"})})//検索時にユニーク性を確保する
public class Patient implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String patientid;//orcaなどのレセコンの患者idと一致させる
@ManyToOne(fetch = FetchType.EAGER, cascade =
CascadeType.PERSIST)//今は ManyToOne で満足しましょう
@JoinColumn(name = "facility_id")
private Facility facility;
private String name1;
private String name1kana;
private String name2;
private String name2kana;
private String name3;
private String name3kana;
private String address_home;
private Double address_parking_lat;
private Double address_parking_lng;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPatientid() {
return patientid;
}
public void setPatientid(String patientid) {
this.patientid = patientid;
}
public Facility getFacility() {
return facility;
}
public void setFacility(Facility facility) {
this.facility = facility;
}
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName1kana() {
return name1kana;
}
public void setName1kana(String name1kana) {
this.name1kana = name1kana;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public String getName2kana() {
return name2kana;
}
public void setName2kana(String name2kana) {
this.name2kana = name2kana;
}
public String getName3() {
return name3;
}
public void setName3(String name3) {
this.name3 = name3;
}
public String getName3kana() {
return name3kana;
}
public void setName3kana(String name3kana) {
this.name3kana = name3kana;
}
public String getAddress_home() {
return address_home;
}
public void setAddress_home(String address_home) {
this.address_home = address_home;
}
public Double getAddress_parking_lat() {
return address_parking_lat;
}
public void setAddress_parking_lat(Double address_parking_lat) {
this.address_parking_lat = address_parking_lat;
}
public Double getAddress_parking_lng() {
return address_parking_lng;
}
public void setAddress_parking_lng(Double address_parking_lng) {
this.address_parking_lng = address_parking_lng;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id
fields are not set
if (!(object instanceof Patient)) {
return false;
}
Patient other = (Patient) object;
if ((this.id == null && other.id != null) || (this.id != null
&& !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
public String toString() {
return "info.phazor.model.Patient[ id=" + id + " ]";
}
}
2025年3月19日(水) 21:40 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
>
> ああ、間違ってた。
>
> result= result + "<p>"+parking[i].id + ", " + parking[i].latitude +",
> " +parking[i].longitude + "</p>";
>
> ですね。
> ここ何をやっているかというと・・・
> indexedDB の PatientMap データベースの parking テーブルを配列に直し
> 全てのレコードの id, latitude, longitude カラムを表示させているわけです。
>
> 緯度経度情報を持っていると何かと便利なのですが、
> 訪問診療向けの電子カルテでも直接緯度経度を取り扱うようには
> なってないですね。
>
> 例えば、以下のデータが得られたとする。
> id, lat, lng
> 1, 35.46716680741217, 139.62126181269232
> 2, 35.45128156486365, 139.63234465164888
> 3, 35.449827221108556, 139.63414915664472
> 4, 35.44772513690715, 139.6445303045752
> 5, 35.445403859432815, 139.6494486976317
> これをそのまま .csv で保存。
>
> google マイマップ
> https://www.google.co.jp/intl/ja/maps/about/mymaps/
> でこの .csv から新しい地図を作成。
>
> google map を開いてサイドバーの「保存済み」-> マイマップ
> からこの地図を読み込むことができます。
>
> すると google map 上に 1-5 の位置にマーカーが表示されます。
> (横浜駅前〜桜木町駅〜横浜市役所〜山下公園)
>
> これで、緯度経度情報が google map と連携できたわけです。
>
> ナビ機能などは google map に任せてしまった方がいいので、
> 私の用途ではこれで十分。
>
> 試しに訪問診療始めてみたいなんてケースではこんなもので
> 十分かなと。
>
>
> 猪股弘明
> 精神科医
>
>
> 2025年3月18日(火) 5:32 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
> >
> > >本来は、こうやって取得した駐車場位置は電子カルテに格納させるのが
> > >いいんでしょうが、ドライバーさんが使うだけの場合もあるので
> > >ブラウザのデータベースに押し込んでおくだけでもいいかも。
> >
> > indexedDB というのを使えばよかったんですね。
> > https://phazor.jp/map3/
> > に設置。
> >
> > indexedDB は直接扱うのはややこしいようなんですが、dexie という
> > ユーティリティ使うと楽なようです。
> >
> > var db = new Dexie('PatientMap');
> > db.version(1).stores({
> > parking: "++id, latitude, longtitude"
> > });
> >
> > これだけで、データベース PatientMap とテーブル parking が
> > 使えるようになります。
> >
> >
> > 猪股
> >
> > 2025年3月16日(日) 16:29 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
> > >
> > > とりあえず、端末現在位置を抜いてきてブラウザ上に表示。
> > > https://phazor.jp/map2/
> > >
> > > 本来は、こうやって取得した駐車場位置は電子カルテに格納させるのが
> > > いいんでしょうが、ドライバーさんが使うだけの場合もあるので
> > > ブラウザのデータベースに押し込んでおくだけでもいいかも。
> > >
> > >
> > > 猪股
> > >
> > > 2025年2月23日(日) 19:42 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
> > > >
> > > > 長谷川さん
> > > >
> > > > tbl_ptinf -> comment_1, comment_2 は完全に空いてますね。
> > > >
> > > > が、
> > > > 住所、住所(経度緯度)、駐車場(経度緯度)
> > > > を永続化するとそれだけでカラム二つ使ってしまうので、余裕がない。
> > > > 電子カルテもそうですが、新規に(ptid は同一番号で連携している)患者データベース起こした方が
> > > > いいでしょうね。
> > > >
> > > > 本来は、「訪問診療用」を謳っている電子カルテにこういった機能があればいいんでしょうが
> > > > 勤務先のスタッフが使いきれていないのか、それとももともと機能自体がないのか
> > > > よくわかりませんが、地図的な情報をわかりやすく閲覧できるようにはなってないですね。
> > > >
> > > >
> > > > 猪股
> > > >
> > > > 2025年2月23日(日) 17:56 Hasegawa Tsukasa <hasegawa@xxxxxxx>:
> > > > >
> > > > > 猪股先生
> > > > > いつもお世話になります。スカイエスエイッチ長谷川です。
> > > > >
> > > > > 患者様の特別な情報は、患者登録のコメント、その他、連絡先などに
> > > > > 登録しています。患者様の緯度経度は、この辺に登録し、アプリで
> > > > > 読み出すといいかもしれません。
> > > > > 当社では、この辺に登録した内容を活用する以下のカスタマイズが
> > > > > あります。
> > > > > ・会計時、患者さんに使えたい特別なメモ印刷。
> > > > > タクシーを呼んでほしいなどもあり。
> > > > > ・患者さんの希望する薬局にORCAから直接PC-FAX送付
> > > > > ・オンライン診療の患者さんに処方箋イメージを先行メール
> > > > >
> > > > > > ところで、こういう問題が面白いのは、問題自体の
> > > > > > 面白さもさることながら、その施設での情報の取り扱いなども
> > > > > > 浮き彫りになるから。
> > > > > >
> > > > > > google map API では、位置情報として経度緯度を使った方が精度良く計算できそうですが、
> > > > > > 一般の医療機関で患者居宅を経度緯度で持っている施設はないでしょう。
> > > > > > 使えそうなのは、電子カルテやレセコンに記録している住所情報ですが、ORCA で
> > > > > > 住所情報がどこに記録されているかわかりますか?
> > > > > > ORCA では、tbl_ptinf テーブルの home_post, home_adrs, home_banti カラムに格納されている
> > > > > > 情報が住所情報にあたると思います。
> > > > > >
> > > > > > もちろん、これでも十分ではなく、訪問時に居宅玄関に車を乗り入れるわけにもいかないので
> > > > > > 実際には、駐車場所の情報などもどこかに記録されていると思います。
> > > > > >
> > > > > > だから、訪問先のルートを決定するなどなどの問題を検討するにあたっては、
> > > > > > 「少なくとも住所情報を複数持てるデータベースを含んだアプリを新規に起こして統合する」
> > > > > > のがベターだと思うんですが、これは私も未着手。
> > > > > >
> > > > > > しかし、この手の話題は、現場では必要になると思うんですが、
> > > > > > 話題になること自体が少ないですね。
> > > > > >
> > > > > >
> > > > > > 2025年2月22日(土) 13:41 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
> > > > > >
> > > > > > >
> > > > > > > 巡回セールスマン問題に google map 関連 API が使えそうだとわかったところで
> > > > > > > 簡単なテストコードを書く。
> > > > > > >
> > > > > > > JavaScript なら、例えば以下のリクエストを API に投げる。
> > > > > > >
> > > > > > > var request = {
> > > > > > > origin: new google.maps.LatLng(XXX, YYY), // 出発地
> > > > > > > destination: new google.maps.LatLng(XXX, YYY), // 目的地
> > > > > > > waypoints: [ // 経由地点
> > > > > > > { location: new google.maps.LatLng(35.50996314910701,
> > > > > > > 139.63138680450265) },//菊名駅
> > > > > > > { location: new google.maps.LatLng(35.44346801137525,
> > > > > > > 139.64004655227728) },//横浜スタジアム
> > > > > > > { location: new google.maps.LatLng(35.50731141811835,
> > > > > > > 139.61738567344364) },//新横浜駅
> > > > > > > ],
> > > > > > > travelMode: google.maps.DirectionsTravelMode.DRIVING,
> > > > > > > //optimizeWaypoints: true,
> > > > > > > avoidHighways: true,
> > > > > > > };
> > > > > > >
> > > > > > > 実行結果は https://x.com/air_h_128k_ilI/status/1891323906474049972/photo/1 の通り。
> > > > > > > 横浜駅前→菊名駅→横浜スタジアム→新横浜駅→横浜駅前
> > > > > > > というルートですね。この時の所要時間は(高速を使って) 74 分。
> > > > > > >
> > > > > > > optimizeWaypoints: true のコメントを外すと最適化ができて
> > > > > > > https://x.com/air_h_128k_ilI/status/1891323906474049972/photo/2
> > > > > > > という結果になります。
> > > > > > > 横浜駅前→新横浜駅→菊名駅→横浜スタジアム→横浜駅前
> > > > > > > が、グーグル先生が提案してくれたルートで所要時間 54分となかなか
> > > > > > > 短縮されてます。
> > > > > > >
> > > > > > >
> > > > > > > 猪股
> > > > > > >
> > > > > > > 2025年2月22日(土) 13:08 Hiroaki Inomata <inomatah0612@xxxxxxxxx>:
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > あまり投稿のない訪問診療界隈の話題。
> > > > > > > >
> > > > > > > > ・訪問診療と電子処方箋
> > > > > > > >
> > > > > > > > かなり難しい。
> > > > > > > > 電子カルテはインターネット空間にあるのがほとんど。
> > > > > > > > 一方、オン資端末の先にあるのは閉域網。
> > > > > > > > このままだと出先のカルテ端末から、オン資端末に連絡をつけること自体が
> > > > > > > > できないので、何らかの工夫をする必要がある。
> > > > > > > > ベンダーからリモート署名で解決する予定だというアナウンスがあったが、
> > > > > > > > いつになるか未定で、それくらいならば、標準型電子カルテの
> > > > > > > > 登場を待ってからでもよさそう。
> > > > > > > >
> > > > > > > > ちなみにマイナ保険証の確認にはマイナ資格確認アプリ
> > > > > > > > https://apps.apple.com/jp/app/%E3%83%9E%E3%82%A4%E3%83%8A%E8%B3%87%E6%A0%BC%E7%A2%BA%E8%AA%8D%E3%82%A2%E3%83%97%E3%83%AA/id6468985377?platform=iphone
> > > > > > > > (iPhone)
> > > > > > > > https://play.google.com/store/apps/details?id=go.ssk.oqs.oqs_mobile_app&hl=ja
> > > > > > > > (android)
> > > > > > > > というものを使う。
> > > > > > > > (当院、導入まだ)
> > > > > > > >
> > > > > > > >
> > > > > > > > ・巡回セールスマン問題
> > > > > > > >
> > > > > > > > こちらはなかなか面白い(と思う)。
> > > > > > > > 訪問診療というからには
> > > > > > > > 「一定期間(2週に1回か月に2回)内に、曜日毎に経由地点を変えた上で、
> > > > > > > > 全患者宅を効率よく巡回する」
> > > > > > > > 必要があるが、これをロジックのみで完全に解くのはかなり難しい。
> > > > > > > > そこで、問題をもうちょっと簡略化して
> > > > > > > > 「ある日に、訪問予定先はあらかじめわかっていて、それら患者宅を漏れなく
> > > > > > > > 1回訪問するとして、最も効率的な(時間or距離)経路は何か」
> > > > > > > > に方針変更する。
> > > > > > > > これはいわゆる巡回セールスマン問題
> > > > > > > > https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E3%82%BB%E3%83%BC%E3%83%AB%E3%82%B9%E3%83%9E%E3%83%B3%E5%95%8F%E9%A1%8C
> > > > > > > > というやつです。
> > > > > > > > 有名問題に帰着できるとその解決策はあちこちで提案されていて、例えば、
> > > > > > > > 『Directions API で巡回セールスマン問題を解決する』
> > > > > > > > https://maps.multisoup.co.jp/blog/1016/
> > > > > > > > なんて記事もあったりします。
> > > > > > > >
> > > > > > > >
> > > > > > > > (続く)
> > > > > > > > 猪股
> > > > >
> > > > > ★★★☆☆☆★★★☆☆☆★★★☆☆☆★★★☆☆☆
> > > > > 株式会社スカイ・エス・エイッチ http://www.sky.sh/
> > > > > 日医総研日医IT認定サポート事業所
> > > > > 長谷川 司 hasegawa@xxxxxxx
> > > > > 京都市伏見区新町5丁目495北本ビル4F401
> > > > > TEL 075-622-7385 FAX 075-622-7403
> > > > >