1 /*
2 This file is part of Qadastre.
3 Copyright (C) 2010 Pierre Ducroquet <pinaraf@pinaraf.info>
4
5 Qadastre is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Qadastre is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Qadastre. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "cadastrewrapper.h"
20 #include <QUrl>
21 #include <QDebug>
22 #include <QNetworkReply>
23 #include <QNetworkRequest>
24 #include <QWebPage>
25 #include <QWebFrame>
26 #include <QWebElement>
27 #include <QDesktopServices>
28 #include <QDir>
29 #include <QFile>
30 #include <QApplication>
31 #include <QList>
32 #include <QSettings>
33 #include <QImage>
34 #include <QColor>
35
36 CadastreWrapper *CadastreWrapper::m_instance = 0;
37
instance()38 CadastreWrapper *CadastreWrapper::instance()
39 {
40 if (!CadastreWrapper::m_instance)
41 CadastreWrapper::m_instance = new CadastreWrapper;
42 return CadastreWrapper::m_instance;
43 }
44
CadastreWrapper(QObject * parent)45 CadastreWrapper::CadastreWrapper(QObject *parent) :
46 QObject(parent)
47 , m_networkManager(0)
48 , m_gotCookie(false)
49 {
50 setRootCacheDir(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
51 }
52
setNetworkManager(QNetworkAccessManager * aManager)53 void CadastreWrapper::setNetworkManager(QNetworkAccessManager *aManager)
54 {
55 if (m_networkManager)
56 disconnect(m_networkManager, 0, this, 0);
57 m_networkManager = aManager;
58 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkFinished(QNetworkReply*)));
59 m_networkManager->get(QNetworkRequest(QUrl("http://www.cadastre.gouv.fr/scpc/accueil.do")));
60 }
61
searchVille(const QString & city,const QString & department)62 void CadastreWrapper::searchVille(const QString &city, const QString &department)
63 {
64 // {"numerovoie": "", "indiceRepetition": "", "nomvoie": "", "lieuDit": "", "ville": city.upper(), "codePostal": "", "codeDepartement": dept, "nbResultatParPage": 20, "x": 0, "y" : 0}
65 QString data = QString("numerovoie=&indiceRepetition=&nomvoie=&lieuDit=&ville=%1&codePostal=&codeDepartement=%2&nbResultatParPage=20&x=0&y=0")
66 .arg(QString::fromLatin1(QUrl::toPercentEncoding(city.toUpper())))
67 .arg(department);
68 qDebug() << data;
69 qDebug() << data.toLatin1();
70 qDebug() << m_networkManager;
71 m_networkManager->post(QNetworkRequest(QUrl("http://www.cadastre.gouv.fr/scpc/rechercherPlan.do")), data.toLatin1());
72 }
73
searchCode(const QString & code,const QString & department)74 void CadastreWrapper::searchCode(const QString &code, const QString &department)
75 {
76 // {"numerovoie": "", "indiceRepetition": "", "nomvoie": "", "lieuDit": "", "ville": city.upper(), "codePostal": "", "codeDepartement": dept, "nbResultatParPage": 20, "x": 0, "y" : 0}
77 QString data = QString("numerovoie=&indiceRepetition=&nomvoie=&lieuDit=&codeCommune=%1&codeDepartement=%2&nbResultatParPage=20&x=0&y=0")
78 .arg(QString::fromLatin1(QUrl::toPercentEncoding(code.toUpper())))
79 .arg(department);
80 qDebug() << data;
81 qDebug() << data.toLatin1();
82 qDebug() << m_networkManager;
83 m_networkManager->post(QNetworkRequest(QUrl("http://www.cadastre.gouv.fr/scpc/rechercherPlan.do")), data.toLatin1());
84 }
85
requestCity(const QString & code)86 City CadastreWrapper::requestCity(const QString &code)
87 {
88 qDebug() << "Request city: " << code;
89 QDir cache = m_cacheDir;
90 QNetworkReply *reply = m_networkManager->get(QNetworkRequest(QUrl("http://www.cadastre.gouv.fr/scpc/afficherCarteCommune.do?c=" + code)));
91 while (!reply->isFinished())
92 qApp->processEvents();
93 cache.cd(code);
94 QSettings raw_city(cache.absoluteFilePath("cache.ini"), QSettings::IniFormat);
95 City result(code);
96 result.setName(raw_city.value("name").toString());
97 result.setDepartement(raw_city.value("department").toString());
98 result.setGeometry(raw_city.value("geometry").toRect());
99 result.setProjection(raw_city.value("projection").toString());
100 return result;
101 }
102
tileFile(const QString & code,int row,int column)103 QString CadastreWrapper::tileFile(const QString &code, int row, int column)
104 {
105 QDir cache = m_cacheDir;
106 cache.cd(code);
107 QString fileName = QString("%1-%2.png").arg(row).arg(column);
108 return cache.absoluteFilePath(fileName);
109 }
110
downloadTiles(City city)111 bool CadastreWrapper::downloadTiles(City city)
112 {
113 m_progress = new QProgressDialog();
114 m_progress->setWindowTitle(QApplication::tr("Downloading tiles..."));
115 m_progress->setMaximum(city.tileRows() * city.tileColumns());
116 m_progress->setMinimum(0);
117 m_progress->setValue(0);
118 m_progress->show();
119
120 QDir cache = m_cacheDir;
121 cache.cd(city.code());
122
123 for (int r = 0 ; r < city.tileRows() ; ++r) {
124 for (int c = 0 ; c < city.tileColumns() ; ++c) {
125 QString fileName = QString("%1-%2.png").arg(r).arg(c);
126 qDebug() << fileName;
127 if (cache.exists(fileName) && QFileInfo(cache, fileName).size()) {
128 // the file already exists, cool !
129 } else {
130 QRect rect = city.tileGeometry(r, c);
131 QString bbox = QString("%1.0,%2.0,%3.0,%4.0").arg(rect.left()).arg(rect.bottom()).arg(rect.right()).arg(rect.top());
132 fileName = cache.absoluteFilePath(fileName);
133 m_waitingTiles[fileName] = city.tileGeometry(r, c);
134 }
135 }
136 }
137 m_progress->setMaximum(m_waitingTiles.count());
138 m_startTime = QDateTime::currentDateTime();
139 while (m_waitingTiles.count() > 0 && !m_progress->wasCanceled()) {
140 QString fileName = m_waitingTiles.begin().key();
141 QRect rect = m_waitingTiles.begin().value();
142 m_waitingTiles.take(fileName);
143 QString bbox = QString("%1.0,%2.0,%3.0,%4.0").arg(rect.left()).arg(rect.top()).arg(rect.right()).arg(rect.bottom());
144 QString url = QString("http://www.cadastre.gouv.fr/scpc/wms?version=1.1&request=GetMap&layers=CDIF:LS3,CDIF:LS2,CDIF:LS1,CDIF:PARCELLE,CDIF:NUMERO,CDIF:PT3,CDIF:PT2,CDIF:PT1,CDIF:LIEUDIT,CDIF:COMMUNE&format=image/png&bbox=%1&width=600&height=600&exception=application/vnd.ogc.se_inimage&styles=LS3_90,LS2_90,LS1_90,PARCELLE_90,NUMERO_90,PT3_90,PT2_90,PT1_90,LIEUDIT_90,COMMUNE_90").arg(bbox);
145 qDebug() << url;
146 m_pendingTiles[m_networkManager->get(QNetworkRequest(QUrl(url)))] = fileName;
147 while (m_pendingTiles.count() > 0 && !m_progress->wasCanceled()) {
148 qApp->processEvents();
149 }
150 }
151
152 bool ret = true;
153 if (m_progress->wasCanceled())
154 ret = false;
155 else {
156 QSettings settings(cache.absoluteFilePath("cache.ini"), QSettings::IniFormat);
157 settings.setValue("complete", true);
158 settings.sync();
159 }
160
161 m_progress->hide();
162 m_progress->deleteLater();
163 m_progress = NULL;
164
165 return ret;
166 }
167
networkFinished(QNetworkReply * reply)168 void CadastreWrapper::networkFinished(QNetworkReply *reply)
169 {
170 if (m_pendingTiles.contains(reply)) {
171 QFile target(m_pendingTiles[reply]);
172 QByteArray ba = reply->readAll();
173
174 // white -> transparent
175 QImage img;
176 img.loadFromData(ba);
177 QImage img2 = img.convertToFormat(QImage::Format_ARGB32);
178 Q_ASSERT(img2.hasAlphaChannel());
179 int w=0;
180 for (int y=0; y<img2.height(); ++y) {
181 for (int x=0; x<img2.width(); ++x) {
182 QColor col = QColor(img2.pixel(x, y));
183 if (col == QColor(255, 255, 255)) {
184 col.setAlpha(0);
185 img2.setPixel(x, y, col.rgba());
186 ++w;
187 }
188 }
189 }
190 //Full transparent
191 if (w == img2.height()*img2.width()) {
192 img2 = QImage(1, 1, QImage::Format_ARGB32);
193 img2.setPixel(0, 0, QColor(0, 0, 0, 0).rgba());
194 }
195
196 target.open(QIODevice::WriteOnly);
197 // target.write(reply->readAll());
198 img2.save(&target, "PNG");
199 target.close();
200 m_pendingTiles.remove(reply);
201 if (m_progress) {
202 m_progress->setValue(m_progress->value()+1);
203 if (m_progress->value() > 10) {
204 double ms = m_startTime.secsTo(QDateTime::currentDateTime());
205 double us = ms/m_progress->value();
206 int tot = us*(m_progress->maximum() - m_progress->value());
207
208 if (tot<3600)
209 m_progress->setLabelText(tr("Downloaded: %1/%2\nRemaining time: %3:%4").arg(m_progress->value()).arg(m_progress->maximum()).arg(int(tot/60)).arg(int(tot%60), 2, 10, QChar('0')));
210 else
211 m_progress->setLabelText(tr("Downloaded: %1/%2\nRemaining time: %3:%4:%5").arg(m_progress->value()).arg(m_progress->maximum()).arg(int(tot/3600)).arg(int((tot%3600)/60), 2, 10, QChar('0')).arg(int(tot%60), 2, 10, QChar('0')));
212
213 } else
214 m_progress->setLabelText(tr("Downloaded: %1/%2").arg(m_progress->value()).arg(m_progress->maximum()));
215 }
216 } else if (reply->url() == QUrl("http://www.cadastre.gouv.fr/scpc/accueil.do")) {
217 qDebug() << "Ok, I've got a cookie... I LOVE COOKIES.";
218 reply->readAll();
219 m_gotCookie = true;
220 } else if (reply->url() == QUrl("http://www.cadastre.gouv.fr/scpc/rechercherPlan.do")) {
221 QString pageData = reply->readAll();
222 qDebug() << pageData;
223 QWebPage parsedPage(this);
224 QWebFrame *frame = parsedPage.mainFrame();
225 frame->setHtml(pageData);
226 QWebElement codeCommune = frame->findFirstElement("#codeCommune");
227 QMap<QString, QString> results;
228 if (!codeCommune.isNull()) {
229 // If there is a codeCommune object in the DOM, it means that the search was not successfull.
230 if (codeCommune.tagName().toLower() != "select") {
231 qDebug() << "Invalid page ???";
232 return;
233 }
234 QWebElementCollection options = codeCommune.findAll("option");
235 foreach (QWebElement option, options) {
236 if (!option.attribute("value").isEmpty())
237 results[option.attribute("value")] = option.toPlainText();
238 }
239 } else {
240 // We may have been successfull, who knows ?
241 QString name = frame->findFirstElement("#ville").attribute("value");
242 QWebElementCollection links = frame->findAllElements(".resultat > .parcelles a");
243 QRegExp linkRE("c=(\\w+)");
244 foreach (QWebElement link, links) {
245 QString js = link.attribute("onclick");
246 int pos = linkRE.indexIn(js);
247 if (pos > -1) {
248 results[linkRE.cap(1)] = name;
249 }
250 }
251 }
252 qDebug() << results;
253 emit(resultsAvailable(results));
254 } else if (reply->url().toString().startsWith("http://www.cadastre.gouv.fr/scpc/afficherCarteCommune.do?c=")) {
255 qDebug() << "Got a result !";
256 QString pageData = reply->readAll();
257 if (pageData.isEmpty())
258 return;
259 // qDebug() << pageData;
260 QString name, code, projection;
261 code = reply->url().queryItemValue("c");
262 qDebug() << code;
263 bool inGeoBox = false;
264 QList<int> raw_geometry;
265 foreach (QString line, pageData.split('\n')) {
266 line = line.trimmed();
267 if (name.isEmpty()) {
268 if (line.contains("<title>"))
269 if (line.split(" : ").count() > 1)
270 name = line.split(" : ")[1].split(" - ")[0];
271 }
272 if (projection.isEmpty()) {
273 if (line.contains("projectionName")) {
274 QRegExp reg("<span id=\"projectionName\">(.+)</span>");
275 reg.setMinimal(true);
276 if (reg.indexIn(line) > -1) {
277 projection = reg.cap(1);
278 qDebug() << projection;
279 if (projection.compare("RGF93CC42", Qt::CaseInsensitive) == 0)
280 projection = "+title=Projection conique conforme Zone 1 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=42.000000000 +lon_0=3.000000000 +lat_1=41.250000000 +lat_2=42.750000000 +x_0=1700000.000 +y_0=1200000.000 +units=m +no_defs";
281 else if (projection.compare("RGF93CC43", Qt::CaseInsensitive) == 0)
282 projection = "+title=Projection conique conforme Zone 2 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=43.000000000 +lon_0=3.000000000 +lat_1=42.250000000 +lat_2=43.750000000 +x_0=1700000.000 +y_0=2200000.000 +units=m +no_defs";
283 else if (projection.compare("RGF93CC44", Qt::CaseInsensitive) == 0)
284 projection = "+title=Projection conique conforme Zone 3 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=44.000000000 +lon_0=3.000000000 +lat_1=43.250000000 +lat_2=44.750000000 +x_0=1700000.000 +y_0=3200000.000 +units=m +no_defs";
285 else if (projection.compare("RGF93CC45", Qt::CaseInsensitive) == 0)
286 projection = "+title=Projection conique conforme Zone 4 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=45.000000000 +lon_0=3.000000000 +lat_1=44.250000000 +lat_2=45.750000000 +x_0=1700000.000 +y_0=4200000.000 +units=m +no_defs";
287 else if (projection.compare("RGF93CC46", Qt::CaseInsensitive) == 0)
288 projection = "+title=Projection conique conforme Zone 5 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=46.000000000 +lon_0=3.000000000 +lat_1=45.250000000 +lat_2=46.750000000 +x_0=1700000.000 +y_0=5200000.000 +units=m +no_defs";
289 else if (projection.compare("RGF93CC47", Qt::CaseInsensitive) == 0)
290 projection = "+title=Projection conique conforme Zone 6 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=47.000000000 +lon_0=3.000000000 +lat_1=46.250000000 +lat_2=47.750000000 +x_0=1700000.000 +y_0=6200000.000 +units=m +no_defs";
291 else if (projection.compare("RGF93CC48", Qt::CaseInsensitive) == 0)
292 projection = "+title=Projection conique conforme Zone 7 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=48.000000000 +lon_0=3.000000000 +lat_1=47.250000000 +lat_2=48.750000000 +x_0=1700000.000 +y_0=7200000.000 +units=m +no_defs";
293 else if (projection.compare("RGF93CC49", Qt::CaseInsensitive) == 0)
294 projection = "+title=Projection conique conforme Zone 8 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=49.000000000 +lon_0=3.000000000 +lat_1=48.250000000 +lat_2=49.750000000 +x_0=1700000.000 +y_0=8200000.000 +units=m +no_defs";
295 else if (projection.compare("RGF93CC50", Qt::CaseInsensitive) == 0)
296 projection = "+title=Projection conique conforme Zone 9 +proj=lcc +towgs84=0.0000,0.0000,0.0000 +a=6378137.0000 +rf=298.2572221010000 +lat_0=50.000000000 +lon_0=3.000000000 +lat_1=49.250000000 +lat_2=50.750000000 +x_0=1700000.000 +y_0=9200000.000 +units=m +no_defs";
297 else if (projection.compare("LAMB1", Qt::CaseInsensitive) == 0)
298 projection = "+title=Lambert I +proj=lcc +nadgrids=ntf_r93.gsb,null +towgs84=-168.0000,-60.0000,320.0000 +a=6378249.2000 +rf=293.4660210000000 +pm=2.337229167 +lat_0=49.500000000 +lon_0=0.000000000 +k_0=0.99987734 +lat_1=49.500000000 +x_0=600000.000 +y_0=200000.000 +units=m +no_defs";
299 else if (projection.compare("LAMB2", Qt::CaseInsensitive) == 0)
300 projection = "+title=Lambert II +proj=lcc +nadgrids=ntf_r93.gsb,null +towgs84=-168.0000,-60.0000,320.0000 +a=6378249.2000 +rf=293.4660210000000 +pm=2.337229167 +lat_0=46.800000000 +lon_0=0.000000000 +k_0=0.99987742 +lat_1=46.800000000 +x_0=600000.000 +y_0=200000.000 +units=m +no_defs";
301 else if (projection.compare("LAMB3", Qt::CaseInsensitive) == 0)
302 projection = "+title=Lambert III +proj=lcc +nadgrids=ntf_r93.gsb,null +towgs84=-168.0000,-60.0000,320.0000 +a=6378249.2000 +rf=293.4660210000000 +pm=2.337229167 +lat_0=44.100000000 +lon_0=0.000000000 +k_0=0.99987750 +lat_1=44.100000000 +x_0=600000.000 +y_0=200000.000 +units=m +no_defs";
303 else if (projection.compare("LAMB4", Qt::CaseInsensitive) == 0)
304 projection = "+title=Lambert IV +proj=lcc +nadgrids=ntf_r93.gsb,null +towgs84=-168.0000,-60.0000,320.0000 +a=6378249.2000 +rf=293.4660210000000 +pm=2.337229167 +lat_0=42.165000000 +lon_0=0.000000000 +k_0=0.99994471 +lat_1=42.165000000 +x_0=234.358 +y_0=185861.369 +units=m +no_defs";
305 }
306 }
307 }
308 if (inGeoBox) {
309 raw_geometry.append(line.split(".")[0].split(")")[0].toInt());
310 if (line.contains(')'))
311 inGeoBox = false;
312 }
313 if (line == "new GeoBox(")
314 inGeoBox = true;
315 }
316 if (!raw_geometry.size())
317 return;
318 qDebug() << raw_geometry;
319 QRect geometry(raw_geometry[0], raw_geometry[1], raw_geometry[2]-raw_geometry[0], raw_geometry[3]-raw_geometry[1]);
320 qDebug() << geometry;
321 QDir cache = m_cacheDir;
322 if (!cache.exists(code))
323 cache.mkdir(code);
324 cache.cd(code);
325 qDebug() << cache.absoluteFilePath("cache.ini");
326 QSettings settings(cache.absoluteFilePath("cache.ini"), QSettings::IniFormat);
327 settings.setValue("name", name);
328 settings.setValue("geometry", geometry);
329 settings.setValue("department", name.mid(name.lastIndexOf('(')+1, name.lastIndexOf(')')-name.lastIndexOf('(')-1));
330 settings.setValue("projection", projection);
331 settings.sync();
332 }
333 reply->close();
334 }
335
setRootCacheDir(QDir dir)336 void CadastreWrapper::setRootCacheDir(QDir dir)
337 {
338 m_cacheDir = dir;
339 if (!m_cacheDir.cd("qadastre")) {
340 m_cacheDir.mkdir("qadastre");
341 m_cacheDir.cd("qadastre");
342 }
343 }
344
getCacheDir()345 QDir CadastreWrapper::getCacheDir()
346 {
347 return m_cacheDir;
348 }
349