home: ricerca live su tutti i comuni italiani (7894, centroide OSM) + 16 porti; lista vuota finché non digiti; normalizzazione accenti/apostrofi; Invio = ricerca online estero
This commit is contained in:
+2
-1
@@ -15,7 +15,8 @@ OTHER_FILES += \
|
|||||||
qml/harbour-tide.qml \
|
qml/harbour-tide.qml \
|
||||||
qml/pages/*.qml \
|
qml/pages/*.qml \
|
||||||
qml/cover/*.qml \
|
qml/cover/*.qml \
|
||||||
qml/js/*.js \
|
qml/js/tides.js \
|
||||||
|
qml/js/comuni.js \
|
||||||
rpm/harbour-tide.spec \
|
rpm/harbour-tide.spec \
|
||||||
rpm/harbour-tide.desktop \
|
rpm/harbour-tide.desktop \
|
||||||
translations/harbour-tide.ts \
|
translations/harbour-tide.ts \
|
||||||
|
|||||||
+7898
File diff suppressed because it is too large
Load Diff
+109
-124
@@ -1,13 +1,12 @@
|
|||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import Sailfish.Silica 1.0
|
import Sailfish.Silica 1.0
|
||||||
import "../js/tides.js" as Tides
|
import "../js/tides.js" as Tides
|
||||||
|
import "../js/comuni.js" as Comuni
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
property var customPorts: [] // porti creati dalla ricerca libera (sessione)
|
// Campi di ricerca FUORI dall'header della lista: gli id dichiarati nel
|
||||||
|
|
||||||
// Campi di ricerca FUORI dal header della lista: gli id dichiarati nel
|
|
||||||
// componente header hanno scope chiuso e le funzioni del Page non li
|
// componente header hanno scope chiuso e le funzioni del Page non li
|
||||||
// vedono (ReferenceError "filter is not defined").
|
// vedono (ReferenceError "filter is not defined").
|
||||||
Column {
|
Column {
|
||||||
@@ -20,36 +19,11 @@ Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SearchField {
|
SearchField {
|
||||||
id: filter
|
id: search
|
||||||
placeholderText: qsTr("Filter ports…")
|
placeholderText: qsTr("Search a port or a town…")
|
||||||
onTextChanged: rebuild()
|
onTextChanged: updateList()
|
||||||
}
|
EnterKey.enabled: text.trim().length > 0
|
||||||
|
EnterKey.onClicked: findOnline()
|
||||||
Item { width: parent.width; height: Theme.paddingMedium }
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: geoField
|
|
||||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
placeholderText: qsTr("Free search: any city (online)…")
|
|
||||||
EnterKey.enabled: text.length > 0
|
|
||||||
EnterKey.onClicked: findCity()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
text: qsTr("Find")
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
onClicked: findCity()
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: geoMsg
|
|
||||||
visible: false
|
|
||||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
color: Theme.errorColor
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,18 +33,18 @@ Page {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
model: ListModel { id: portModel }
|
model: ListModel { id: resultModel }
|
||||||
|
|
||||||
header: SectionHeader { text: qsTr("Ports") }
|
|
||||||
|
|
||||||
footer: Item {
|
footer: Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: portModel.count === 0 ? Theme.itemSizeMedium : 0
|
height: resultModel.count === 0 ? Theme.itemSizeSmall * 1.6 : 0
|
||||||
Label {
|
Label {
|
||||||
visible: portModel.count === 0
|
visible: resultModel.count === 0
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.horizontalPageMargin
|
anchors.margins: Theme.horizontalPageMargin
|
||||||
text: qsTr("No ports match the filter. Try the free city search above.")
|
text: search.text.trim() === ""
|
||||||
|
? qsTr("Type to search among all Italian towns and known ports.")
|
||||||
|
: qsTr("No local results — press Enter to search online.")
|
||||||
color: Theme.secondaryColor
|
color: Theme.secondaryColor
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
@@ -113,66 +87,81 @@ Page {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("[tide] open port:", model.name, "custom:", model.custom)
|
console.log("[tide] open result:", model.name, "kind:", model.kind)
|
||||||
// model.port (array JS) non è esposto dai ListModel su Qt 5.6:
|
page.openResult(model.kind, model.portIndex, model.name, model.lat, model.lon)
|
||||||
// si ricostruisce il porto dalle strutture JS tramite l'indice.
|
|
||||||
var p = model.custom ? page.customPorts[model.customIndex]
|
|
||||||
: Tides.PORTS[model.realIndex]
|
|
||||||
page.openDetail(p, model.realIndex, model.custom)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticalScrollDecorator {}
|
VerticalScrollDecorator {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rebuild() {
|
// --- Ricerca live (porti noti + tutti i comuni italiani) ---
|
||||||
portModel.clear()
|
function norm(s) {
|
||||||
var f = filter.text.toLowerCase()
|
// minuscolo + apostrofi tipografici -> dritti + accenti rimossi
|
||||||
var pref = appSettings.preferredPort
|
// ("citta" trova "Città", "sant'angelo" trova "Sant'Angelo")
|
||||||
var i, p
|
return s.toLowerCase()
|
||||||
|
.replace(/[\u2018\u2019]/g, "'")
|
||||||
|
.replace(/[àáâãä]/g, "a")
|
||||||
|
.replace(/[èéêë]/g, "e")
|
||||||
|
.replace(/[ìíîï]/g, "i")
|
||||||
|
.replace(/[òóôõö]/g, "o")
|
||||||
|
.replace(/[ùúûü]/g, "u")
|
||||||
|
}
|
||||||
|
|
||||||
|
function rangeOf(cost) {
|
||||||
|
return "±" + (cost[1][0] + cost[2][0]).toFixed(2) + " m"
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateList() {
|
||||||
|
var q = norm(search.text.trim())
|
||||||
|
resultModel.clear()
|
||||||
|
if (q === "") return
|
||||||
|
var i, p, name
|
||||||
|
// porti noti (match su nome o paese)
|
||||||
for (i = 0; i < Tides.PORTS.length; i++) {
|
for (i = 0; i < Tides.PORTS.length; i++) {
|
||||||
p = Tides.PORTS[i]
|
p = Tides.PORTS[i]
|
||||||
if (f !== "" && (p[0] + " " + p[1]).toLowerCase().indexOf(f) === -1)
|
if (norm(p[0] + " " + p[1]).indexOf(q) !== -1) {
|
||||||
continue
|
resultModel.append({
|
||||||
portModel.append({
|
name: p[0], country: p[1],
|
||||||
name: p[0], country: p[1],
|
range: rangeOf(p[4]),
|
||||||
range: "±" + (p[4][1][0] + p[4][2][0]).toFixed(2) + " m",
|
kind: "port", portIndex: i, lat: 0, lon: 0,
|
||||||
realIndex: i, customIndex: -1, custom: false,
|
isPreferred: (appSettings.preferredPort === p[0])
|
||||||
isPreferred: (pref === p[0])
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < customPorts.length; i++) {
|
// comuni italiani (max 25 risultati)
|
||||||
p = customPorts[i]
|
var n = 0
|
||||||
if (f !== "" && p[0].toLowerCase().indexOf(f) === -1)
|
for (i = 0; i < Comuni.COMUNI.length && n < 25; i++) {
|
||||||
continue
|
name = Comuni.COMUNI[i][0]
|
||||||
portModel.append({
|
if (norm(name).indexOf(q) !== -1) {
|
||||||
name: p[0],
|
var lat = Comuni.COMUNI[i][1]
|
||||||
country: p[1] !== "" ? p[1] : qsTr("searched city"),
|
var lon = Comuni.COMUNI[i][2]
|
||||||
range: "±" + (p[4][1][0] + p[4][2][0]).toFixed(2) + " m",
|
resultModel.append({
|
||||||
realIndex: -1, customIndex: i, custom: true,
|
name: name, country: qsTr("Italy"),
|
||||||
isPreferred: (pref === p[0])
|
range: rangeOf(Tides.nearestPort(lat, lon).port[4]),
|
||||||
})
|
kind: "comune", portIndex: -1, lat: lat, lon: lon,
|
||||||
|
isPreferred: false
|
||||||
|
})
|
||||||
|
n++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigazione centralizzata: pageStack si risolve solo nel contesto del
|
// Navigazione centralizzata: pageStack si risolve solo nel contesto del
|
||||||
// Page (nei delegate della lista non è visibile su questo kit).
|
// Page (nei delegate della lista non è visibile su questo kit).
|
||||||
function openDetail(port, portIndex, custom) {
|
function openDetail(port, portIndex, custom) {
|
||||||
console.log("[tide] openDetail:", port[0], "custom:", custom)
|
console.log("[tide] openDetail:", port ? port[0] : "undefined", "custom:", custom)
|
||||||
pageStack.push(Qt.resolvedUrl("DetailPage.qml"),
|
pageStack.push(Qt.resolvedUrl("DetailPage.qml"),
|
||||||
{ port: port, portIndex: portIndex, custom: custom })
|
{ port: port, portIndex: portIndex, custom: custom })
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Ricerca libera (geocoding) ---
|
function openResult(kind, portIndex, name, lat, lon) {
|
||||||
function geoFail(msg) {
|
if (kind === "port") {
|
||||||
geoMsg.text = msg || qsTr("City not found (offline and not in local dataset).")
|
page.openDetail(Tides.PORTS[portIndex], portIndex, false)
|
||||||
geoMsg.visible = true
|
return
|
||||||
geoMsg.color = Theme.errorColor
|
}
|
||||||
}
|
// comune italiano: costanti dal porto noto entro 222 km, altrimenti
|
||||||
|
// stima sintetica da lat/lon (etichettata nella pagina di dettaglio)
|
||||||
function geoFound(name, lat, lon, src) {
|
|
||||||
console.log("[tide] geoFound:", name, lat, lon, src)
|
|
||||||
if (!name) { geoFail(); return }
|
|
||||||
var np = Tides.nearestPort(lat, lon)
|
var np = Tides.nearestPort(lat, lon)
|
||||||
var cost, flag
|
var cost, flag
|
||||||
if (np.km < 222) {
|
if (np.km < 222) {
|
||||||
@@ -182,24 +171,23 @@ Page {
|
|||||||
cost = Tides.synthConst(lat, lon)
|
cost = Tides.synthConst(lat, lon)
|
||||||
flag = "synth"
|
flag = "synth"
|
||||||
}
|
}
|
||||||
var port = [name, "", lat, lon, cost, flag]
|
page.openDetail([name, qsTr("Italy"), lat, lon, cost, flag], -1, true)
|
||||||
customPorts.push(port)
|
}
|
||||||
// voce del porto di riferimento con la distanza dalla città cercata
|
|
||||||
if (np.port !== null && np.port[0].toLowerCase() !== name.toLowerCase()) {
|
// --- Ricerca online (geocoding Photon) per città estere / non trovate ---
|
||||||
customPorts.push([np.port[0],
|
function geoFound(name, lat, lon, src) {
|
||||||
"(" + Math.round(np.km) + " km da " + name + ")",
|
console.log("[tide] geoFound:", name, lat, lon, src)
|
||||||
np.port[2], np.port[3], np.port[4].slice(), np.port[5] || ""])
|
if (!name) return
|
||||||
}
|
var np = Tides.nearestPort(lat, lon)
|
||||||
rebuild()
|
var cost, flag
|
||||||
geoMsg.text = qsTr("Found %1 (via %2)").arg(name).arg(src)
|
if (np.km < 222) {
|
||||||
geoMsg.visible = true
|
cost = np.port[4].slice()
|
||||||
geoMsg.color = Theme.highlightColor
|
flag = "near:" + np.port[0] + ":" + (np.km / 111).toFixed(1)
|
||||||
try {
|
} else {
|
||||||
page.openDetail(port, -1, true)
|
cost = Tides.synthConst(lat, lon)
|
||||||
} catch (e) {
|
flag = "synth"
|
||||||
console.log("[tide] push DetailPage failed:", e)
|
|
||||||
geoFail(qsTr("Could not open the detail page: %1").arg(e))
|
|
||||||
}
|
}
|
||||||
|
page.openDetail([name, "", lat, lon, cost, flag], -1, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function geoXhr(query, cb) {
|
function geoXhr(query, cb) {
|
||||||
@@ -213,7 +201,7 @@ Page {
|
|||||||
if (!done) {
|
if (!done) {
|
||||||
done = true
|
done = true
|
||||||
console.log("[tide] geoXhr timeout")
|
console.log("[tide] geoXhr timeout")
|
||||||
cb(null, true)
|
cb(null)
|
||||||
}
|
}
|
||||||
}, 12000)
|
}, 12000)
|
||||||
xhr.open("GET", "https://photon.komoot.io/api/?limit=1&osm_tag=place:city&q="
|
xhr.open("GET", "https://photon.komoot.io/api/?limit=1&osm_tag=place:city&q="
|
||||||
@@ -229,11 +217,9 @@ Page {
|
|||||||
var j = JSON.parse(xhr.responseText)
|
var j = JSON.parse(xhr.responseText)
|
||||||
if (j && j.features && j.features.length > 0) {
|
if (j && j.features && j.features.length > 0) {
|
||||||
var f = j.features[0]
|
var f = j.features[0]
|
||||||
var name = f.properties.name || query
|
cb(f.properties.name || query,
|
||||||
cb(name,
|
|
||||||
f.geometry.coordinates[1],
|
f.geometry.coordinates[1],
|
||||||
f.geometry.coordinates[0],
|
f.geometry.coordinates[0])
|
||||||
"OpenStreetMap")
|
|
||||||
} else {
|
} else {
|
||||||
cb(null)
|
cb(null)
|
||||||
}
|
}
|
||||||
@@ -246,38 +232,37 @@ Page {
|
|||||||
xhr.send()
|
xhr.send()
|
||||||
}
|
}
|
||||||
|
|
||||||
function findCity() {
|
function findOnline() {
|
||||||
var q = geoField.text.trim()
|
var q = search.text.trim()
|
||||||
console.log("[tide] findCity:", q)
|
console.log("[tide] findOnline:", q)
|
||||||
if (q === "") return
|
if (q === "") return
|
||||||
geoMsg.text = qsTr("Searching…")
|
// se è un porto noto, apri direttamente
|
||||||
geoMsg.visible = true
|
var ql = norm(q)
|
||||||
geoMsg.color = Theme.secondaryColor
|
|
||||||
// la città cercata è già un porto noto? aprilo direttamente
|
|
||||||
var ql = q.toLowerCase()
|
|
||||||
for (var i = 0; i < Tides.PORTS.length; i++) {
|
for (var i = 0; i < Tides.PORTS.length; i++) {
|
||||||
if (Tides.PORTS[i][0].toLowerCase() === ql) {
|
if (norm(Tides.PORTS[i][0]) === ql) {
|
||||||
geoMsg.visible = false
|
|
||||||
page.openDetail(Tides.PORTS[i], i, false)
|
page.openDetail(Tides.PORTS[i], i, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var loc = Tides.findCityLocal(q)
|
// se è un comune italiano esatto, apri direttamente
|
||||||
if (loc) {
|
for (i = 0; i < Comuni.COMUNI.length; i++) {
|
||||||
geoFound(loc[0], loc[2], loc[3], qsTr("local dataset"))
|
if (norm(Comuni.COMUNI[i][0]) === ql) {
|
||||||
} else {
|
page.openResult("comune", -1, Comuni.COMUNI[i][0],
|
||||||
geoXhr(q, function(name, lat, lon, src, timeout) {
|
Comuni.COMUNI[i][1], Comuni.COMUNI[i][2])
|
||||||
if (timeout) {
|
return
|
||||||
geoFail(qsTr("Geocoding service not reachable (timeout). Check the network."))
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
geoFound(name, lat, lon, src)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
geoXhr(q, function(name, lat, lon) {
|
||||||
|
if (!name) {
|
||||||
|
console.log("[tide] online: non trovato")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
geoFound(name, lat, lon, "OpenStreetMap")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
rebuild()
|
updateList()
|
||||||
var pref = appSettings.preferredPort
|
var pref = appSettings.preferredPort
|
||||||
if (pref !== "") {
|
if (pref !== "") {
|
||||||
for (var i = 0; i < Tides.PORTS.length; i++) {
|
for (var i = 0; i < Tides.PORTS.length; i++) {
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
<message><source>No ports match the filter. Try the free city search above.</source><translation type="unfinished"></translation></message>
|
<message><source>No ports match the filter. Try the free city search above.</source><translation type="unfinished"></translation></message>
|
||||||
<message><source>Geocoding service not reachable (timeout). Check the network.</source><translation type="unfinished"></translation></message>
|
<message><source>Geocoding service not reachable (timeout). Check the network.</source><translation type="unfinished"></translation></message>
|
||||||
<message><source>Could not open the detail page: %1</source><translation type="unfinished"></translation></message>
|
<message><source>Could not open the detail page: %1</source><translation type="unfinished"></translation></message>
|
||||||
|
<message><source>Search a port or a town…</source><translation type="unfinished"></translation></message>
|
||||||
|
<message><source>Type to search among all Italian towns and known ports.</source><translation type="unfinished"></translation></message>
|
||||||
|
<message><source>No local results — press Enter to search online.</source><translation type="unfinished"></translation></message>
|
||||||
|
<message><source>Italy</source><translation type="unfinished"></translation></message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>DetailPage</name>
|
<name>DetailPage</name>
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
<message><source>No ports match the filter. Try the free city search above.</source><translation>Nessun porto corrisponde al filtro. Prova la ricerca libera qui sopra.</translation></message>
|
<message><source>No ports match the filter. Try the free city search above.</source><translation>Nessun porto corrisponde al filtro. Prova la ricerca libera qui sopra.</translation></message>
|
||||||
<message><source>Geocoding service not reachable (timeout). Check the network.</source><translation>Servizio di geocoding non raggiungibile (timeout). Controlla la rete.</translation></message>
|
<message><source>Geocoding service not reachable (timeout). Check the network.</source><translation>Servizio di geocoding non raggiungibile (timeout). Controlla la rete.</translation></message>
|
||||||
<message><source>Could not open the detail page: %1</source><translation>Impossibile aprire la pagina di dettaglio: %1</translation></message>
|
<message><source>Could not open the detail page: %1</source><translation>Impossibile aprire la pagina di dettaglio: %1</translation></message>
|
||||||
|
<message><source>Search a port or a town…</source><translation>Cerca un porto o un comune…</translation></message>
|
||||||
|
<message><source>Type to search among all Italian towns and known ports.</source><translation>Inizia a digitare: cerca tra tutti i comuni italiani e i porti noti.</translation></message>
|
||||||
|
<message><source>No local results — press Enter to search online.</source><translation>Nessun risultato locale — premi Invio per la ricerca online.</translation></message>
|
||||||
|
<message><source>Italy</source><translation>Italia</translation></message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>DetailPage</name>
|
<name>DetailPage</name>
|
||||||
|
|||||||
Reference in New Issue
Block a user