沒穿方服

封存

顯示╱隱藏內文

這篇不是什麼新知,只是我第一次處理,覺得比看起來容易,希望改善「看起來比較難」情況。

村里邊界,右下文字部分要自己處理 mouse 事件

整個處理大致都被 GeoJSON 包裝好了

原本沒碰過 GeoJSON,自己想像要處理區塊框線、互動,以為要折磨很多事;但其實在 Leaflet 裡可以看成載入一個 GeoJSON 資源,再用 addTo(map) 掛上去就結束了。

建立與顯示:

const layer = Leaflet.geoJSON(data as GeoJsonObject, options);
layer.addTo(map);

外觀、互動行為等,可以用 options 去調,以下是一個 options 物件:

{
  style: () => ({
    fill: false,
  }),
  onEachFeature: function (feat: Feature, layer: Path) {
    layer.on('mouseover', function () {
      layer.setStyle({ fill: true });
    });
    layer.on('mouseout', function () {
      layer.setStyle({ fill: false });
    });
  },
}

要關閉圖層,就 remove 掉:

layer.removeFrom(map);


取得地圖資料

另一個問題是圖資怎麼來,首選當然是內政部的開放資料:
縣市界:鄉鎮市區界線(TWD97經緯度)、村(里)界:村里界圖(TWD97經緯度)

但要怎麼轉成 GeoJSON 格式?尋找現成整理好的,似乎都失連,或看起來有點年代,幸好裡面原理沒變,參考 jason2506/Taiwan.TopoJSON,它是用 mapshaper 將 .shp 轉為 .geojson。

具體指令例,縣市:

mapshaper raw/COUNTY_MOI_1090820.shp -simplify interval=400 \
  -filter 'COUNTYNAME !== "金門縣" && COUNTYNAME !== "連江縣"' \
  -rename-fields name=COUNTYNAME -filter-fields name \
  -o format=geojson precision=0.0001 \
  counties.geojson

村里:

mapshaper raw/VILLAGE_NLSC_1130807.shp -simplify interval=30 \
  -rename-fields id=VILLCODE \
  -verbose \
  -filter 'COUNTYNAME !== "金門縣" && COUNTYNAME !== "連江縣"' \
  -each 'name=TOWNNAME + " " + VILLNAME' -filter-fields id,name \
  -o format=geojson precision=0.0001 \
  villages.geojson

其中 -simplify interval=precision 參數可以控制產出的精細度,也決定了檔案大小。 上面範例出來的結果是 61.35 KB 和 7.21 MB,後者檔案很大,但其實掛在 Cloudflare R2,在 HTTP/2 和壓縮加持下傳輸量只有 1.7 MB,使用上是沒什麼問題的。

利用植物偽裝。隨便撿一塊黃牌是方便監視用的,下次可以換成狗比較看不見的紅或綠色 據說王太太也是用烤雞捕狗

今年八月鎖定一隻個性激動,愛吠愛淋雨的大白,基於之前動保處人員說他們也會結紮公犬,會比較乖之類的(有這種事?),就蒐證請求處理,結果又回一些沒用的話,爭取後結論是動保處只能出借誘捕籠,民眾要自己捕捉,誘餌也要自己準備。

在「精準捕捉」這個奇葩政策下,就是幾乎已經放棄捕犬。 反正我也沒親身用過(犬)誘捕籠,聽里長說之前放都被人偷開或破壞,就讓我來實驗看看吧。

結果放了將近一個月(通常大概只能借一個禮拜),曾經目睹大白接近,甚至鑽進去把食物叼出來(身手真好),但最後只抓到另一隻近期新來的乖乖狗,近看才確認是已紮母犬,只好放走,放狗混帳竟是我自己。

尖耳黃臉黑,蠻漂亮的 買鹹酥雞的一晚,加菜是意外

以下重點回顧,下次我有信心成功:

  • 位置很重要:隱密、容易監視、降低狗戒心

    這次我優先考量隱密性(防小人),但放一兩天發現狗不靠近,才換到稍微開放的地方,之後接觸率就增加了。

    一個人搬誘捕籠要用背負、扛的,有點難度,但後來看動保處大哥也是扛出去才拆解上車,所以這作法應該正確。

  • 清空餵食者放的食物

    當時有一位隱藏的大餵食者會深夜偷丟大包雞骨,還有一些零星亂餵的人,因為在誘捕我就清得很勤,結果狗的出現率就遞減,這之間要抓一個平衡,在狗絕跡前速戰速決。

  • 投其所好

    當然要針對狗的個性誘捕,這群狗的偏好第一是雞脖子,再來就是雞骨人丟的骨架內臟,至於狗罐頭、貓食甚至鹹酥雞,似乎都激不起衝動,沒有被吃。

  • 關鍵的掛鏈

    這次最飲恨的就是沒有先摸熟這個機關,連結「踏板的掛鏈」和「掉落門的長桿」的部分,決定了踏板的敏感度,如果這個鏈條掛得太靠外面(籠子外面),會變成踩到踏板也不會觸發關門,大白可能就是這樣才逍遙法外。

從 react-masonry-css 示例

註 1:這篇談的是 2020 前的 CSS 解法 (flexbox),且使用 react-masonry-css 這個老套件,並非新的 JS 解或還未定案的 CSS 標準(參考),目前(2024)看來,舊方法還是有適用的地方。

註 2:能調整的前提是每個元素的高度已知。

以 CSS 為主的解法,看中的是單純性和效能,避免頻繁計算每個元素的實際尺寸,而缺點如下:

  • 排序問題,因為以多個 column「直行」為容器,所以元素其實是由上排到下,而不是由左到右。
    這點在 react-masonry-css 是用 JS 硬把元素分配給各個 column 解決。
  • 高度不均。因為根本沒有考慮元素的高度,當然避免不了有的 column 特別長或短的問題。
    具體可以看 issue #73 中的附圖:very different columns height - paulcollett/react-masonry-css

針對高度不均,造成底部有很大落差不齊的情況,折衷辦法是預先給予每個元素「高度」的參考,類似給每個 <img> 賦予已知的 height 值;然後再用 JS 計算累積高度,最後搬動特別突出的元素,直到無法再用搬動方式改善為止。

實作也很單純,主要價值還是知道多做這一小步,就能得到不少改善。

程式碼參考我的 fork: bootleq/react-masonry-css at forker,具體在 balanceColumnsmoveOddItem 兩個函數:
react-masonry-css/src/react-masonry-css.js at 4866d3596#L72

從來沒貢獻過 Firefox 本體,很久以前嘗試,光是 build 環境要裝 Visual C++ 等開發工具,太麻煩就放棄了。

現在我的環境有 WSL,又見 mozilla 文件已經寫得很詳細,就再來洗頭看看。
文件:Firefox Contributors’ Quick Reference

記錄在 Bug 1905331,功能很小,就是「尋找列」(Findbar) 找不到東西時會有一個 beep 音效,但 PDF 檢視時少了這個聲音,就把它加上去而已:

1905331 - Findbar in PDF viewer doesn't play "not found" sound

這個 bug 也是弄了 3 個月才結案,merge 後再跑 2 個月,11/26 才跟著 Firefox 133 正式上線。

安裝到能 build 起來已經不容易

官方說明(Firefox Source Docs)其實很不錯,更快的方式應該是找 mentor 或在 chat 問路,但我還是先自己試試。
文件:Getting Set Up To Work On The Firefox Codebase

Windows 下會教你用 MozillaBuild 安裝,會建立專用的 shell 設好獨立環境,缺點是會跟本來慣用的 shell 切開,有些東西像 git 也是另外裝一套,但確保跑得起來是第一優先啦,不做抵抗。

具體就是以下指令跑成功,該裝的就裝完了:
python3 bootstrap.py --vcs=git

但初期我還遇到 build script / 原始碼有些檔案沒處理好 python open 編碼的問題,也是追了一下。

然後 git pull 之後就能建自己的 Firefox 了:

./mach build ./mach run

在 WSL 跑 hg-git 慢到不行

WSL 遇過在 Windows 目錄跑執行檔會突然變超慢的問題(參考,把執行檔搬到 WSL 目錄竟然就沒事了),用前面裝的環境就剛好迎面撞上。

另 Firefox 原始碼還是用 Mercurial 做版本控制,如果要用 git 就要裝 hg-git 來中介,加上原始碼規模不小,結果一些 git 指令都要等非常久,久到中間一定要做別的事才行。

好用的 SearchFox

Mozilla 的程式碼搜尋,還支援 blame,重點是真的快。
https://searchfox.org/

哪那麼多 Findbar 呀

2008 年寫過 Firefox 3.0 的擴充套件,當時也改過 Findbar,記得 XUL 原始碼只有 gBrowser 裡面有 findbar,很輕鬆就能變造它的外觀和行為。

現在就不一樣了,tabbrowser(全域的 gBrowser)裡面有從某處初始化 findbar 的邏輯,widgets/browser-custom-element 裡面有視情況建立 FinderParent (remoteBrowser 時) 或一般 Finder (non-remote) 的分歧, 然後 PDF 的 viewer 模組裡又有自己的 findbar,這東西還有部分是從 pdfjs repo 不定期複製過來的,……各種各種。

這邊 trace code 和認識多年來的架構變更、引進概念(例如 fission 是什麼、modalHighlight 是什麼)也是個門檻。 還好 findbar 是相對冷門的功能,本身變動沒有很多。

Phabricator 協作平台

類似工具我之前主要是用 GitLab,還是第一次知道 Phabricator(音同 fabricator),配合 moz-phab 這個指令用起來很方便。

除了 Phabricator 本身有很多協作功能外,在 commit message 和遠端 branch 管理方面也可以省很多心力, 因為新的 branch 在用 moz-phab push 時就會自動生成對應的 Differential 頁面(跟本來的 branch name 已經無關),並把訊息塞到 commit message, 之後如果 review 來回要做 force push 也沒關係,只要在 commit message 保留 Differential Revision: .../D215196 這段文字,所有記錄就會回到同一頁,reviewer 也能看到你硬改了哪些東西。

Random fail 的測試

推上 Phabricator 的東西並不會跑所有測試,我這次接觸到的 CI 主要在程式要 land(類似所謂 merge 進主線)的時候,會由有權限的人 Pushing to Try,我才上去看測試死在哪,例如這具屍體

之前工作時很頭痛的 random fail 問題,或稱 intermittent test / flaky test,就是平常跑都會過,偏偏就偶爾會死掉卡住 CI,這種要找出死因可能很花時間,於是常常不了了之。

這邊見識到 Firefox 的作法是有一個小組會去抓出案例,研究後登記起來,也提供查詢: Sheriffed intermittent failures

然後跑測試時有一個 verify flag,對特定測試重複跑很多次,判斷是 intermittent 還是真的錯誤。

用 chat 求助

其實這個 bug 真正實作和討論的時間並不長,中間拖比較久是 reviewer 擺著,最後在 chat 求助才有人幫 land。

Extension API 的 Find API

原來跟瀏覽器的 findbar 根本沒關係呀!

之前一直想動 Findbar,很想從這裡下手,仔細研究才看懂走不通。