Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: monthly
target-branch: "develop"

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: monthly
target-branch: "develop"
20 changes: 20 additions & 0 deletions .github/workflows/compat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Compatibility Check

on: [push, pull_request]

jobs:
compat:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -r requirements.txt
- run: python -c "import server.app"
working-directory: ${{ github.workspace }}
env:
JAGEOCODER_SERVER_URL: "https://jageocoder.info-proto.com/jsonrpc"
13 changes: 13 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Docker Build

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
push: false
14 changes: 14 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Lint

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install flake8
- run: flake8 server/
870 changes: 479 additions & 391 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "jageocoder-server"
description = "Web service providing Japanese address geocoding functionality."
version = "1.0.13"
version = "1.0.14"
authors = ["Takeshi Sagara <sagara@info-proto.com>"]
repository = "https://github.com/t-sagara/jageocoder-server/"
license = "The MIT License"
Expand Down
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
charset-normalizer==3.4.2
Flask==2.3.3
charset-normalizer==3.4.6
Flask==3.1.3
Flask-Cors==5.0.0
Flask-JSONRPC==2.2.2
Flask-JSONRPC==3.0.1
geographiclib==2.1
jageocoder==2.2.0
python-dotenv==1.1.0
python-dotenv==1.2.0
waitress==2.1.2
7 changes: 5 additions & 2 deletions server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,12 @@ def _parse_degree(val: str) -> Tuple[float, str]:

nodes = [x["candidate"] for x in results]
return render_template(
'node_list.html',
'rgeocode.html',
tree=g.tree,
nodes=nodes)
nodes=nodes,
lon=lon,
lat=lat,
)


@app.route("/aza/<aza_id>", methods=['POST', 'GET'])
Expand Down
2 changes: 1 addition & 1 deletion server/csvmatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io
from logging import getLogger
import re
from typing import Any, Dict, List, Sequence
from typing import Any, Dict, List

import charset_normalizer
from flask import render_template, request, stream_with_context
Expand Down
114 changes: 114 additions & 0 deletions server/static/js/mapcontrol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
function createMapControl(map, jsonLayers, showNodeLink) {

function renderCandidates(data) {
let div = document.getElementById('address');
if (div && data.length > 0) {
let nearest = data[0].candidate;
div.innerHTML = '「<a href="' + showNodeLink.replace("0", nearest.id)
+ '">' + nearest.fullname.join(' ')
+ '</a>」付近';
}

if (jsonLayers.length > 0) {
for (const layer of jsonLayers) {
map.off('click', layer.id);
map.removeLayer(layer.id);
}
map.removeSource('triangle');
jsonLayers = [];
}

let points = [];
for (const i in data) {
const node = data[i]['candidate'];
let merged = false;
for (let j = 0; j < points.length; j++) {
if (points[j].geometry.coordinates[0] == node.x
&& points[j].geometry.coordinates[1] == node.y) {
points[j].properties.names += ', ' + node.fullname.join('');
merged = true;
break;
}
}
if (!merged) {
points.push({
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [node.x, node.y]
},
'properties': {
'id': node.id,
'names': node.fullname.join('')
}
});
}
}
map.addSource('triangle', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': points
}
});

jsonLayers.push({
'id': 'nodes',
'source': 'triangle',
'type': 'circle',
'paint': {
'circle-stroke-color': '#FFFFFF',
'circle-stroke-width': 2,
'circle-stroke-opacity': 1,
'circle-radius': 8,
'circle-color': '#1e50a2',
'circle-opacity': 0.5
},
'filter': ['==', '$type', 'Point']
});
for (const layer of jsonLayers) {
map.addLayer(layer);
map.on('click', layer.id, function (e) {
const coordinates = e.features[0].geometry.coordinates.slice();
let names = e.features[0].properties.names.toString();
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(names)
.addTo(map);
});
map.on('mouseenter', layer.id, function () {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', layer.id, function () {
map.getCanvas().style.cursor = '';
});
}
}

function reverseGeocoding(lat, lng, zoom) {
let level = parseInt(zoom, 10) - 7;
if (level < 1) {
level = 1;
}
fetch('/jsonrpc', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'jageocoder.reverse',
params: {x: lng, y: lat, level: level},
id: 1
})
}).then(res => res.json()
).then(rpcResponse => {
if (rpcResponse.error) {
console.error('JSONRPC error:', rpcResponse.error);
return;
}
let data = rpcResponse.result;
renderCandidates(data);
});
}

return { renderCandidates, reverseGeocoding };
}
110 changes: 5 additions & 105 deletions server/templates/node.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ <h2>下位住所ノード</h2>
{% endblock %}

{% block script %}
<script src="{{ url_for('static', filename='js/mapcontrol.js') }}"></script>
<script>
let mapdiv = document.getElementById('map');
mapdiv.style.display = 'none';
Expand Down Expand Up @@ -154,117 +155,16 @@ <h2>下位住所ノード</h2>
[{{ node.x }}, {{ node.y }}]
).addTo(map);

const { reverseGeocoding } = createMapControl(
map, jsonLayers, "{{ url_for('show_node', id='0') }}"
);

function onDragEnd() {
let lnglat = marker.getLngLat();
let zoomlevel = map.getZoom();
reverseGeocoding(lnglat.lat, lnglat.lng, zoomlevel);
}

function reverseGeocoding(lat, lng, zoom) {
let level = parseInt(zoom, 10) - 7;
if (level < 1) {
level = 1;
}
let formData = new FormData();
formData.append("lat", lat);
formData.append("lon", lng);
formData.append("level", level);

let response = fetch('{{ url_for('reverse_geocode') }}', {
method: 'POST',
body: formData
}).then(res => res.json()
).then(data => {
let div = document.getElementById('address');
let showNodeLink = "{{ url_for('show_node', id='0') }}";
if (data.length > 0) {
let nearest = data[0].candidate;
div.innerHTML = '「<a href="' + showNodeLink.replace("0", nearest.id)
+ '">' + `${data[0].candidate.fullname.join(' ')}`
+ '</a>」付近';
}

if (jsonLayers.length > 0) {
for (const layer of jsonLayers) {
map.off('click', layer.id);
map.removeLayer(layer.id);
}
map.removeSource('triangle');
jsonLayers = [];
}

let points = [];
let nodes = {};
for (const i in data) {
const vertice = data[i];
const node = vertice['candidate'];
nodes[node.id] = node.fullname;
let merged = false;
for (let j = 0; j < points.length; j++) {
if (points[j].geometry.coordinates[0] == node.x
&& points[j].geometry.coordinates[1] == node.y) {
points[j].properties.names += ', ' + node.fullname.join('');
merged = true;
break;
}
}
if (!merged) {
points.push({
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [node.x, node.y]
},
'properties': {
'id': node.id,
'names': node.fullname.join('')
}
});
}
}
source = {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': points
}
};
map.addSource('triangle', source);

jsonLayers.push({
'id': 'nodes',
'source': 'triangle',
'type': 'circle',
'paint': {
'circle-stroke-color': '#FFFFFF',
'circle-stroke-width': 2,
'circle-stroke-opacity': 1,
'circle-radius': 8,
'circle-color': '#1e50a2',
'circle-opacity': 0.5
},
'filter': ['==', '$type', 'Point']
});
for (const layer of jsonLayers) {
map.addLayer(layer);
map.on('click', layer.id, function (e) {
const coordinates = e.features[0].geometry.coordinates.slice();
let names = e.features[0].properties.names.toString();
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(names)
.addTo(map);
});
map.on('mouseenter', layer.id, function () {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', layer.id, function () {
map.getCanvas().style.cursor = '';
});
}
});
}

marker.on('dragend', onDragEnd);
{% endif %}
</script>
Expand Down
Loading
Loading