DS=-dsco NAME="Youshi Industrial Park grid addressing and street naming exercise" \ -dsco DESCRIPTION="See $(subst $(HOME)/,https://www.,$(PWD))" ## Author: Dan Jacobson https://www.jidanni.org/ ## Copyright: https://www.gnu.org/licenses/gpl.html ## Created: 2026-04-03T22:00:51+0000 ## Last-Updated: 2026-06-02T15:59:13+0000 ## Update #: 1853 D=$(HOME)/Downloads DD=$D/d/pandoc TR=$(DD)/translations/zh-TW.yaml ### Conference documentation part. They wanted a DOCX. youshi.browse: youshi_zh.html: youshi_zh.browse: slides.show: %.show:%.html; $${BROWSER?} $< %.math_check:%.txt;! fgrep -nH \$$ $< #Math didn't show up in DOCX so don't dare use it. zh2en0:; @perl -pwl \ -e 's/(\d+)至(\d+)號/Nos. $$1-$$2/g; s/對面/opposite /g;' \ -e 's/(\d+)路/Rd. $$1/g; s/(\d+)號/No. $$1/g;' zh2en1:; @perl -nwl \ -e '/(\d+)號/ && push @p, "No. $$1";' \ -e '/(\d+)衖/ && push @p, "Sub alley $$1";' \ -e '/(\d+)弄/ && push @p, "Alley $$1";' \ -e '/(\d+)巷/ && push @p, "Lane $$1";' \ -e '/(\d+)街/ && push @p, "Street $$1";' \ -e '/(\d+)路/ && push @p, "Road $$1";' \ -e 'print qq{@p}; undef @p;' #one address per input line please en2zh1:; @perl -nwl \ -e 'push @p,"$$1路" if /Road\s+(\d+)/;' \ -e 'push @p,"$$1街" if /Street\s+(\d+)/;' \ -e 'push @p,"$$1巷" if /Lane\s+(\d+)/;' \ -e 'push @p,"$$1弄" if /Alley\s+(\d+)/;' \ -e 'push @p,"$$1衖" if /Sub alley\s+(\d+)/;' \ -e 'push @p,"$$1號" if /(?:No\.|№)\s+(\d+)/;' \ -e 'print @p; undef @p;' #one address per input line please reference-docx-proof-of-lack: pandoc --print-default-data-file reference.docx |\ pandoc --from docx+styles --to markdown # https://github.com/jgm/citeproc/blob/master/locales/zh-TW.xml # https://forums.zotero.org/post/editdiscussion/0/262465 #Workaround newzh=\ \ \ \ 取自\ \ \ #https://www.zotero.org/styles/apa-6th-edition dan6.csl:$(DD)/apa-6th-edition.csl perl -pwle 'if(/xml:lang="ro">/){print q( $(newzh));}' $< > $@ BB=--csl dan6.csl --citeproc --bibliography=dan.bib --metadata link-citations=true mini=積丹尼-路號門牌弧形網-\(摘要\) quanwen=積丹尼-路號門牌弧形網-\(全文\) mini.txt:youshi_z0.txt; perl -nwle \ 'exit if /::: extra/; next if /House|Road|Folded/; print;' $< > $@ mini.docx: BB= mini.docx: fils=indent.lua mini:mini.docx; mv $< $D/$(mini).docx; : now go look at it xmllook:youshi.docx set -eu; m=/tmp/gg; mkdir $$m; mv $< $$m; cd $$m && unzip $< find /tmp/gg -ls -type f ! -name \*.png ! -name \*.docx \ -exec tidy -i -xml -q {} \; docx:youshi.docx mv $< $D/$(quanwen).docx : Now launch 'Word' upon $D/$(quanwen).docx %.fetch: curl --insecure https://api.github.com/repos/$d/$*/releases/latest | \ grep browser_download_url.*$$(dpkg --print-architecture).deb |\ cut -d \" -f 4 | xargs wget --no-directories --no-verbose --show-progress %.install:%.fetch sudo dpkg -i $**.deb mv -v $**.deb /tmp %.upgrade:%.install; pandoc.upgrade:d=jgm; panache.upgrade:d=jolars go=$< #go=$$(wwwize -c $(PWD)/$<\#goal) %.browse:%.html; $${BROWSER?} $(go) zap_footnotes.lua:; echo "function Note(el) return {} end" > $@ zap_images.lua:; echo "function Image(el) return {} end" > $@ zap_en-US.lua:; echo > $@ \ "function Div(el) if el.attributes['lang'] == 'en-US' "\ "then return {} end return el end" extra.lua: echo >$@ 'function Div(el) if el.classes[1] =='\ '"extra" then return {} end return el end' Linter=%.linted $(Linter):%.txt panache lint --message-format short --check --flavor pandoc $< && touch $@ pand=--from=markdown+east_asian_line_breaks --output=$@ \ --fail-if-warnings=true --verbose --strip-comments=true $(BB) fils=extra.lua zap_en-US.lua zap_footnotes.lua indent.lua pzh=--standalone=true $(addprefix --lua-filter=,$(fils)) $< \ --metadata=Abstract-title:摘要 \ --metadata=lang:zh-TW --data-dir=$(DD) $(pand) $(dir $(TR)):; mkdir -p $@ $(TR):; pandoc --output $@ --print-default-data-file=translations/zh-Hant.yaml %_z0.txt:%.txt %.math_check perl -Mutf8 -C -nwle \ 'BEGIN{use warnings q!FATAL!; $$a=0};' \ -e 's/^## /$$&.substr(q{壹貳參肆伍},$$a++,1).q{、}/e if $$a < 5;' \ -e 's!(^#.*\S)\s*/.*!$$1!;' \ -e 'next if /^ #en_on/../^ #en_off/; print;' \ $< >$@ jing=\# indent.lua: ii=function Para (para) \ local text = pandoc.utils.stringify(para); \ if $(jing)para.c == 1 and para.c[1].t == "Image" then return para end; \ if $(jing)para.c == 1 and para.c[1].t == "Link" then return para end; \ if (text:find("圖", 1, true) ~= 1) \ and (text:find("www.jidanni", 1, true) == nil) \ then table.insert(para.content, 1, pandoc.Str \ ("\u{3000}\u{3000}")) end return para end indent.lua:; echo '$(ii)' > $@ needs=$(Linter) $(TR) dan.bib dan6.csl zh_needs=$(needs) $(fils) youshi_zh.native2: %.native2:%.native; ascii2uni -Z '\%d' $< > $@ && cat $@ %_zh.native:%_z0.txt $(zh_needs); pandoc $(pzh) %_zh.html:%_z0.txt $(zh_needs) pandoc $(pzh) test $< -ot $@ %.docx:%_z0.txt $(zh_needs) pandoc $(pzh) test $< -ot $@ %_ze.txt:%.txt %.math_check; perl -nwle 'next if /#en_/; print;' $< > $@ %.html:%_ze.txt $(needs) dan.js pandoc $(pand) $< --standalone=true \ --number-sections \ --shift-heading-level-by=-1 --metadata=abstract-title:"摘要 / Abstract" \ --metadata=title:"路號門牌弧形網 A curved road number - house address grid" \ --metadata=author:'$(au)' --include-in-header=dan.js test $< -ot $@ au=積丹尼 (Dan Jacobson) ### GIS part: d=$D/d/data #"84" indicates WGS84 files SA=1000#The value of our single axis! road84=$d/youshi_roads.geojson addr84=$d/youshi_addrs.geojson include ../../../../utilities/integer_graticule.makefile h654=house_L654_1456_652.kml close_view : $(h654) route.kml; $V medium_view: $(h654) graticule_k_00.kml houses_ai1_named_vertical.kml; $V far_view : $(h654) graticule_k_00.kml edge.kml; $V mgr.png:Makefile make -nd kitty_korner_view | make2graph | dot -Tpng -o $@ $$BROWSER $@ && sleep 11 kitty_korner.vrt: graticule_k_00.gpkg \ house_L2500_2502_500.gpkg house_L500_2500_498.gpkg $O -single -nln $(basename $@) kitty_korner.gpkg:kitty_korner.vrt $R -clipsrc 2495 490 2510 505 -nlt GEOMETRYCOLLECTION kitty_korner_view: kitty_korner.kml; $V KLL=../../../../utilities/kml_line_labels youshi.kmz: graticule_k_00.kml edge.kml $O -f LIBKML $(DS) --config LIBKML_EXTRUDE_FIELD= #The following is optional, an attempt to help Google Earth users see the line labels: unzip $@ set -eu; for i in layers/*grat*; do \ cp $$i $$i.bak; $(KLL) $$i.bak > $$i.xml;\ tidy -xml -i -q $$i.xml > $$i; done zip --freshen $@ rm -rf layers doc.kml edge.gpkg:graticule_k_00.gpkg $R $S "SELECT * FROM $N WHERE name LIKE 1000 OR name LIKE 0" cross.gpkg:graticule_k_00.gpkg $R $S "SELECT 2500 AS name, geom FROM $N WHERE name LIKE 2200 "\ "UNION SELECT 7500 AS name, geom FROM $N WHERE name LIKE 700" quadrants_view: graticule_k_00.kml one_line_x.kml one_line_y.kml; $V _i = 100 #Interval: How many numbers per city block. graticule_k_q.% graticule_k_34.% graticule_k_66.%:\ _w = WHERE (0 + name) <= $(SA) graticule_k_1450.%: _w = WHERE (0 + name) >= $(SA) graticule_k_00.% : _y = y0=0 yi=$(_i) y1=1000 graticule_k_00.% : _x = x0=1000 xi=$(_i) x1=3200 graticule_k_q.% graticule_k_34.% graticule_k_66.%:\ _x = x0=1100 xi=$(_i) x1=1600 graticule_k_1450.%: _y = y0=200 yi=$(_i) y1=700 graticule_k_1450.%: _x = x0=1450 xi=$(_i) x1=1450 graticule_k_34.% : _y = y0=234 yi=$(_i) y1=534 graticule_k_q.% : _y = y0=628 yi=28 y1=656 graticule_k_66.% : _y = y0=266 yi=$(_i) y1=566 #To show how much of the first quadrant of the Cartesian plane we occupy: one_line_x.%: params= y0=0 y1=0 x0=-5000 x1=5000 i=500 name=0 one_line_y.%: params= x0=0 x1=0 y0=-5000 y1=5000 i=500 name=0 wt=WHERE \"addr:housenumber\" LIKE '%號' O=ogrmerge -overwrite_ds -o $@ $^ addr.gpkg:$(addr84) reverse_gcps.txt $R --optfile reverse_gcps.txt -tps $S \ "SELECT DISTINCT \"addr:street\","\ "\"addr:housenumber\", geometry AS geom FROM "\ "$(notdir $N) WHERE \"addr:housenumber\" LIKE '%號'" original_addresses.gpkg:addr.gpkg $R -nlt POINT $S "SELECT DISTINCT printf('%s%c%s',\"addr:street\","\ "CHAR(10), \"addr:housenumber\") AS name, geom FROM $(notdir $N) $(wt)" house_choices.gpkg:addr.gpkg #No AI, just suggestions for a person to choose from $R -nlt POINT $S \ "SELECT DISTINCT printf('%d-%d%c%d|%d',floor(x(geom)),ceiling(x(geom)),"\ "CHAR(10),floor(y(geom)),ceiling(y(geom))) AS name, geom FROM $(notdir $N)" blocks84.gpkg:graticule_k_00.gpkg gcps.txt $R --optfile gcps.txt -tps -a_srs EPSG:4326 $S \ "SELECT geom, name, ST_AsText(geom) AS specs FROM $N" mm=SELECT FORMAT('%4d路(%s): %.0fm',name,specs,$(1)(ST_Length(geom,1))) \ AS minmax FROM $N WHERE name $(2) $(SA) block_lengths:blocks84.gpkg #100 house numbers could equal how many meters? ogr2ogr /vsistdout/ $< -f CSV -lco STRING_QUOTING=IF_NEEDED $S \ "$(call mm,min,<=) UNION $(call mm,max,<=) "\ "UNION $(call mm,min,>=) UNION $(call mm,max,>=)" down=CAST(Y(geom)/$(_i) AS INTEGER) * $(_i) ca=CAST(X(geom) AS INTEGER) oe=IIF(TRUNC(X(geom)) % 2 != q, $(ca), $(ca) + 1) even=$(subst q,0,$(oe)) odd =$(subst q,1,$(oe)) snap_within=15 #We are just going for the low hanging fruit. (Easy cases.) houses_ai0.gpkg:addr.gpkg $R -nlt POINT $S \ "SELECT DISTINCT geom, $(down) AS road,"\ "$(even) AS number FROM $N WHERE Y(geom) % $(_i) <= $(snap_within) UNION "\ "SELECT DISTINCT geom, $(down)+$(_i) AS road,"\ "$(odd) AS number FROM $N WHERE Y(geom) % $(_i) >= $(_i) - $(snap_within)" impossible = X(geom) < $(SA) OR Y(geom) > $(SA) OR Y(geom) < 0 #Beyond $(SA) houses_ai1.gpkg:houses_ai0.gpkg $R -nlt POINT $S "SELECT * FROM $N WHERE NOT ($(impossible))" impossible_addresses.gpkg:addr.gpkg #Points beyond the edge, where (x,y) != (y,x) $R -nlt POINT $S \ "SELECT geom, FORMAT('%.1f/%.1f', X(geom), Y(geom)) AS name "\ "FROM $N WHERE $(impossible)" #road and house number stacked vertically (one column) in label: %_named_vertical.gpkg:%.gpkg $R -nlt POINT $S \ "SELECT geom, FORMAT('%5d路%c%d號', road, CHAR(10), number) AS name FROM $N" #road and house numbers in one row, horizontally in label: %_named_horizontal.gpkg:%.gpkg $R -nlt POINT $S \ "SELECT geom, FORMAT('%d路%d號', road, number) AS name FROM $N" gz=SELECT geometry AS geom, FORMAT('%d/%d', field_2, field_3) AS name FROM $N gcps84.gpkg:gcps.txt $R -if CSV -nlt POINT \ -oo X_POSSIBLE_NAMES=field_4 -oo Y_POSSIBLE_NAMES=field_5 $S "$(gz)" gcps.gpkg:gcps.txt $R -if CSV -nlt POINT \ -oo X_POSSIBLE_NAMES=field_2 -oo Y_POSSIBLE_NAMES=field_3 $S "$(gz)" road_nodes_0_84.gpkg:$(road84) $R -explodecollections -nlt POINTS $S \ "SELECT ST_DissolvePoints(GEOMETRY) AS geom,name FROM $(notdir $N)" road_nodes84.gpkg:road_nodes_0_84.gpkg #These are all the OSM nodes in all the roads $R -nlt POINTS $S "SELECT DISTINCT geom, name FROM $N GROUP BY geom, name" road_nodes_values84.vrt:road_nodes84.gpkg road_values.csv; $O road_nodes_values84.gpkg:road_nodes_values84.vrt $R $S "SELECT DISTINCT road_values.value + 0 AS name,"\ "geom FROM road_values, road_nodes84 "\ "WHERE road_nodes84.name LIKE road_values.name ORDER BY geom" intersections84.gpkg:road_nodes_values84.gpkg $R $S "SELECT t1.geom as geom, t1.name AS n1, t2.name AS n2, "\ "t1.name || '/' || t2.name AS name "\ "FROM $N AS t1, $N AS t2 WHERE t1.geom = t2.geom "\ "AND NOT t1.name = t2.name GROUP BY t1.geom" reverse_gcps.txt:gcps.txt ogr2ogr -if CSV -of CSV /vsistdout/ $< \ -lco SEPARATOR=SPACE -lco STRING_QUOTING=IF_NEEDED -sql \ "SELECT field_1, field_4, field_5, field_2, field_3 FROM $N" |\ grep -- -gcp > $@ gcps.txt:intersections84.gpkg extra_gcps.txt ogr2ogr -of CSV /vsistdout/ $< -lco \ STRING_QUOTING=IF_NEEDED -lco SEPARATOR=SPACE $S \ "SELECT '-gcp', n2 AS x, n1 AS y,"\ "x(geom) AS lon, y(geom) AS lat FROM $N ORDER BY geom"|\ perl -anwle 'next unless /\d/; print;' > $@ cat extra_gcps.txt >> $@ extra_gcps=\ 1000 1000 24.412002 120.645589 \ 1000 700 24.409145 120.643824 extra_gcps.txt: set $(extra_gcps); while test $$# -gt 0; do \ echo -gcp $$1 $$2 $$4 $$3; shift 4; done > $@ route.gpkg: #How to get to house 654 ogr2ogr -nln $(basename $@) $@ :memory: -nlt LINESTRING $S \ "SELECT ST_GeomFromText('LINESTRING(1250 700, 1600 700)') AS geom, 700 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1600 700, 1650 700)') AS geom, 700 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1600 700, 1600 600)') AS geom, 1600 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1600 600, 1450 600)') AS geom, 600 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1450 600, 1450 656)') AS geom, 1450 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1450 656, 1525 656)') AS geom, 656 AS name UNION "\ "SELECT ST_GeomFromText('LINESTRING(1450 656, 1375 656)') AS geom, 656 AS name" GPX1= set -eu; set -- $$(ogrinfo -q $<); shift 2; case $$@ in \(Point\)) n=POINT;;\ *Multi\ Line\ String*) n=MULTILINESTRING;; *Line*) n=LINESTRING;;\ *Polygon*|*Multi*) echo No $$@ allowed in GPX! 1>&2; exit 67;; \ *) echo $< --\> $@: Not ready for type \"$$@\" 1>&2; exit 54;; esac;\ $R -nlt $$n $S 'SELECT name, geom AS geometry FROM "$N"' KML1= $R $S 'SELECT name || "" AS name, geom FROM "$N"' -f LIBKML -dim XY %84.gpx:%84.gpkg; $(GPX1) #WGS84 %84.kml:%84.gpkg; $(KML1) #WGS84 %.gpx:%.gpkg gcps.txt; $(GPX1) --optfile gcps.txt -tps %.kml:%.gpkg gcps.txt; $(KML1) --optfile gcps.txt -tps V= > /tmp/$(VIEWER)errs.txt $(VIEWER) $^ & sleep 11h S=-dialect SQLite -sql R=ogr2ogr $@ $< -nln $(basename $@) N=$(basename $<) VIEWER=viking MAKEFLAGS+=--warn-undefined-variables --no-builtin-variables house_L%.gpkg: #Usage: house_L