Порт сервера на публичный сервер

This commit is contained in:
commit 5c026601e1
63 changed files with 2381 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 SWD Embedded Systems Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

30
README.md Normal file
View File

@ -0,0 +1,30 @@
# gis-map-server
Веб-сервис для распространения и тестовый клиент для отображения картографической информации.
[gis-map-server](src/gis-map-server/) - Python веб-сервер
[gis-client-render](src/gis-client-render) - Qt клиент для отображения
Архитектура проекта:
![GIS Map Server](src/gis-map-server/docs/architecture.drawio.png)
Сервис принимает запросы на отрисовку региона средствами [ПК ЦКИ](https://kpda.ru/products/gis/) и возвращает отрисованную карту в виде картинки (поддерживаемые выходные форматы: bmp, png, jpeg).
Для сборки необходимо:
```
cd src
# Единожды сформировать файл переводов
lrelease gis-client-render/*pro
# Для сборки под архитектуру _cpu_ можно установить CPULIST=_cpu_, например x86
make install
```
В результате в корне проекта появится папка `.install`, в которую инсталлируются runtime-компоненты
При запуске веб-сервер `gis-map-server` считывает [конфигурационный файл](src/gis-map-server/gis-map-server.conf).
`gis-map-server` предоставляет веб-интерфейс, внешний вид которого показан на рисунке 1.
![Рисунок 1](src/gis-map-server/docs/web_interface1.png)
Внешний вид Qt-клиента показан на рисунке 2.
![Рисунок 2](src/gis-client-render/images/client_interface1.png)

2
src/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=LIB
include recurse.mk

View File

@ -0,0 +1,2 @@
LIST=OS
include recurse.mk

View File

@ -0,0 +1,21 @@
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
COMPILER_DRIVER:=
include $(MKFILES_ROOT)/buildlist.mk
ifndef OS
include $(MKFILES_ROOT)/qmacros.mk
endif
USEFILE=
# GIS package options
ifneq ( $(GIS_INSTALL_ROOT), )
DESTDIR:=$(GIS_INSTALL_ROOT)
endif
export GIS_INSTALL_ROOT=$(DESTDIR)
include $(MKFILES_ROOT)/qmake.mk
include $(MKFILES_ROOT)/qtargets.mk

View File

@ -0,0 +1,51 @@
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
qnx {
# Get install directory for defining command to run
INSTALLDIRECTORY=$$(INSTALLDIR)
!isEmpty(INSTALLDIRECTORY) {
target.path = $$(INSTALLDIR)
}
isEmpty(INSTALLDIRECTORY) {
INSTALLDIRECTORY = "/opt/gis/bin"
target.path = $$INSTALLDIRECTORY
message(Manual path setting $$INSTALLDIRECTORY)
}
}
TEMPLATE = app
INSTALLS += target
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += main.cpp\
mainwidget.cpp \
mainwindow.cpp \
image_viewport.cpp \
gismapserver.cpp \
mapsender.cpp
HEADERS += mainwidget.h \
mainwindow.h \
image_viewport.h \
gismapserver.h \
mapsender.h
# If: Cannot find file 'translations/gis-client-render_ru.qm'
# then run lupdate, lrelease manually
TRANSLATIONS += $$_PRO_FILE_PWD_/translations/$$join(TARGET,,,_ru.ts)
RESOURCES += \
images.qrc \
translations.qrc
QMAKE_CXXFLAGS += -std=gnu++11

View File

@ -0,0 +1,52 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "gismapserver.h"
#include "mapsender.h"
QString gis_map_server_get_error_message( GisHttpCodes statusCode )
{
switch ( statusCode ) {
case GisHttpCodes::IsProcessing :
return "Is processing";
case GisHttpCodes::InvalidParameters :
return "Invalid parameters";
case GisHttpCodes::RequestFailed :
return "Failed to request";
case GisHttpCodes::Timeout :
return "Timeout while processing request";
case GisHttpCodes::RenderFailed :
return "Failed to render request";
case GisHttpCodes::Done :
return "Request has been already obtained";
case GisHttpCodes::Ok :
default:
return QString();
}
}
int gis_map_server_request_map( const char *server_url,
double lat,
double lon,
int scale,
int w,
int h,
const char *format,
uint *orderId,
char *pinCode )
{
if ( scale <= 0 || w <= 0 || h <= 0 || !orderId || !format )
return EINVAL;
uint timeout_ms = 30000;
MapSender mapSender( QString(server_url), 0, timeout_ms );
return mapSender.sendRequest( lat, lon, scale, w, h, format, orderId, pinCode );
}
int gis_map_server_get_image( const char *server_url, int orderId, char *pinCode, QImage *image )
{
uint timeout_ms = 30000;
MapSender mapSender( QString(server_url), orderId, timeout_ms );
return mapSender.getImageWithWaiting( pinCode, image );
}

View File

@ -0,0 +1,44 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*************************************************/
/* GIS Map Server */
/* Server interface header */
/*************************************************/
#ifndef GISMAPSENDER_H
#define GISMAPSENDER_H
#ifdef __cplusplus
#include <QImage>
#include <QString>
typedef enum {
Ok = 200,
IsProcessing = 202,
InvalidParameters = 400,
Timeout = 408,
Done = 410,
NoMemory = 418,
RenderFailed = 500,
RequestFailed = 520
} GisHttpCodes;
QString gis_map_server_get_error_message( GisHttpCodes statusCode );
/*
* Make a request to the gis-map-server
*
* format -png, jpeg, bmp
*/
int gis_map_server_request_map(const char *server_url, double lat, double lon, int scale, int w, int h, const char *format, uint* orderId, char *pinCode );
int gis_map_server_get_image(const char *server_url, int orderId, char *pinCode, QImage *image );
#endif
#endif // GISMAPSENDER_H

View File

@ -0,0 +1,92 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "image_viewport.h"
#include <QGraphicsPixmapItem>
#include <QLabel>
#include <QMovie>
#include <QDebug>
ImageViewport::ImageViewport() : QGraphicsView(),
currentMapItem( nullptr )
{
/*
* Animated earth's GIF setup
* Runs until user performs a request
*
* See: https://forum.qt.io/topic/15658/solved-how-to-play-gif-animation-in-qgraphicsview-widget
*/
graphics_scene = new QGraphicsScene();
graphics_scene->setBackgroundBrush( QColor( 239, 239, 239 ) );
QLabel *animation = new QLabel();
QMovie *earth_gif = new QMovie( ":/images/earth.gif" );
animation->setMovie( earth_gif );
earth_gif->setSpeed( 90 );
earth_gif->start();
proxy = graphics_scene->addWidget( animation );
/*
* Placing a circle around GIF to hide its low resolution
*/
QPen earth_bound( (QColor( Qt::black )) );
earth_bound.setWidth( 5 );
graphics_scene->addEllipse( QRectF( 0, 0, 257, 257 ),
QPen( earth_bound ),
QBrush( QColor( 0, 0, 0, 0 ) ) );
setScene( graphics_scene );
setDragMode( QGraphicsView::ScrollHandDrag );
setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
}
void ImageViewport::fit_image()
{
/*
* Place the center of the image at the center of the viewport
*/
centerOn( graphics_scene->width() / 2,
graphics_scene->height() / 2 );
/*
* Keep the whole image inside the viewport, saving the aspect ratio
*/
fitInView( QRectF( 0, 0,
graphics_scene->width(),
graphics_scene->height() ),
Qt::KeepAspectRatio );
}
void ImageViewport::wheelEvent( QWheelEvent *event )
{
if ( event->delta() > 0 )
scale( scroll_step, scroll_step );
if ( event->delta() < 0 )
scale( 1 / scroll_step, 1 / scroll_step );
}
void ImageViewport::show_map_image( const QImage &image )
{
proxy->hide();
if ( currentMapItem )
graphics_scene->removeItem( currentMapItem );
currentMapItem = new QGraphicsPixmapItem( QPixmap::fromImage( image ) );
graphics_scene->addItem( currentMapItem );
fit_image();
}
void ImageViewport::clean_map_image()
{
proxy->show();
if ( currentMapItem ) {
graphics_scene->removeItem( currentMapItem );
currentMapItem = nullptr;
}
fit_image();
}

View File

@ -0,0 +1,34 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef IMAGE_VIEWPORT_H
#define IMAGE_VIEWPORT_H
#include <QEvent>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QWheelEvent>
#include <QGraphicsProxyWidget>
class ImageViewport : public QGraphicsView
{
Q_OBJECT
public:
explicit ImageViewport();
void fit_image();
protected:
virtual void wheelEvent( QWheelEvent *event );
private:
const double scroll_step = 1.3;
QGraphicsScene *graphics_scene;
QGraphicsProxyWidget *proxy;
QGraphicsPixmapItem *currentMapItem;
public slots:
void show_map_image( const QImage &img );
void clean_map_image();
};
#endif // IMAGE_VIEWPORT_H

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>images/earth.gif</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

View File

@ -0,0 +1,64 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <cstring>
#include <unistd.h>
#include <QApplication>
#include <QDebug>
#include <QTranslator>
#include "mainwidget.h"
#include "mainwindow.h"
int main( int argc, char *argv[] )
{
double x_pos = -999, y_pos = -999;
uint32_t width = 0, height = 0, scale = 0;
const char *server_url = nullptr;
const char *image_format = nullptr;
const char *outputImageName = nullptr;
bool consoleMode = false;
QLocale::setDefault(QLocale::C);
int opt = 0;
extern char *optarg;
while ( ( opt = getopt( argc, argv, "x:y:w:h:s:u:f:o:" ) ) != -1 )
{
switch ( opt )
{
case 'x': x_pos = atof( optarg ); break;
case 'y': y_pos = atof( optarg ); break;
case 'w': width = strtoul( optarg, NULL, 0 ); break;
case 'h': height = strtoul( optarg, NULL, 0 ); break;
case 's': scale = strtoul( optarg, NULL, 0 ); break;
case 'u': server_url = strdup( optarg ); break;
case 'f': image_format = strdup( optarg ); break;
case 'o':
{
consoleMode = true;
outputImageName = strdup( optarg );
break;
}
case '?': printf( "Error: unknown option\n" ); exit(1); break;
};
};
QApplication app( argc, argv );
QString ablangStr = getenv( "ABLANG" );
if ( ablangStr.isEmpty() ) {
ablangStr = getenv( "LANG" );
}
QTranslator myTranslator;
myTranslator.load( ":/translations/gis-client-render_" + ablangStr );
app.installTranslator( &myTranslator );
MainWindow window( x_pos, y_pos, width, height, scale, server_url, image_format, outputImageName, consoleMode );
window.show();
return app.exec();
}

View File

@ -0,0 +1,264 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "mainwidget.h"
#include <QApplication>
#include <QDebug>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMutex>
#include <QWaitCondition>
#include <QDir>
#include "gismapserver.h"
MainWidget::MainWidget( double x_pos,
double y_pos,
uint32_t width,
uint32_t height,
uint32_t scale,
const char *server_url,
const char *image_format,
const char *outputImageName,
bool consoleMode ) : QWidget(),
_consoleMode( consoleMode ),
_outputImageName( outputImageName ),
_server_url( server_url ),
_image_format( image_format ),
request_btn_state( true ),
field_x_pos( new QLineEdit() ),
field_y_pos( new QLineEdit() ),
field_width( new QLineEdit() ),
field_height( new QLineEdit() ),
field_scale( new QLineEdit() ),
urlEdit(new QLineEdit()),
formatBox(new QComboBox()),
request_btn( new QPushButton( tr( "Request map" ) ) )
{
/*
* Do not rescale left dock panel's widgets and contents.
*/
QSizePolicy size_policy;
size_policy.setVerticalPolicy( QSizePolicy::Fixed );
size_policy.setHorizontalPolicy( QSizePolicy::Fixed );
setSizePolicy( size_policy );
/*
* Placing console parameters into fields, if they are present
* If they are not present, use placeholders with input hints.
*/
if ( width != 0 )
field_width->setText( QString::number( width, 10 ) );
if ( height != 0 )
field_height->setText( QString::number( height, 10 ) );
if ( x_pos != -999 )
field_x_pos->setText( QString::number( x_pos, 'f', 6 ) );
else
field_x_pos->setPlaceholderText( QString( "[-180...180]" ) );
if ( y_pos != -999 )
field_y_pos->setText( QString::number( y_pos, 'f', 6 ) );
else
field_y_pos->setPlaceholderText( QString( "[-90...90]" ) );
if ( scale != 0 )
field_scale->setText( QString::number( scale, 10 ) );
else
field_scale->setText( "1000000" );
QStringList acceptedFormats;
acceptedFormats << "bmp" << "png" << "jpg";
formatBox->addItems(acceptedFormats);
int idx = formatBox->findText(image_format);
if (idx >= 0)
formatBox->setCurrentIndex(idx);
urlEdit->setText( server_url );
/*
* Acceptable longtitude range: [ -180 ... 180 ].
*/
longtitude_validator = new QDoubleValidator();
longtitude_validator->setRange( -180.0, 180.0 );
longtitude_validator->setDecimals( 8 );
longtitude_validator->setNotation( QDoubleValidator::StandardNotation );
field_x_pos->setValidator( longtitude_validator );
/*
* Acceptable latitude range: [ -90 ... 90 ].
*/
latitude_validator = new QDoubleValidator();
latitude_validator->setRange( -90.0, 90.0 );
latitude_validator->setDecimals( 8 );
latitude_validator->setNotation( QDoubleValidator::StandardNotation );
field_y_pos->setValidator( latitude_validator );
/*
* Acceptable width and height of resulting image in pixels: positive values.
*/
QIntValidator *pixel_validator = new QIntValidator();
pixel_validator->setBottom( 1 );
field_width->setValidator( pixel_validator );
field_height->setValidator( pixel_validator );
/*
* Setting up the left dock panel's vertical layout order.
*/
QVBoxLayout *vlayout = new QVBoxLayout();
QHBoxLayout *hlayout = new QHBoxLayout();
vlayout->addWidget( new QLabel( tr( "Image width (px):" ) ) );
vlayout->addWidget( field_width );
vlayout->addWidget( new QLabel( tr( "Image height (px):" ) ) );
vlayout->addWidget( field_height );
vlayout->addWidget( new QLabel( tr( "Longtitude:" ) ) );
vlayout->addWidget( field_x_pos );
vlayout->addWidget( new QLabel( tr( "Latitude:" ) ) );
vlayout->addWidget( field_y_pos );
vlayout->addWidget( new QLabel( tr( "Scale:" ) ) );
hlayout->addWidget( new QLabel( "1:" ) );
hlayout->addWidget( field_scale );
vlayout->addLayout( hlayout );
vlayout->addWidget( new QLabel( tr( "URL:" ) ) );
vlayout->addWidget( urlEdit );
vlayout->addWidget( new QLabel( tr( "Format:" ) ) );
vlayout->addWidget(formatBox);
vlayout->addWidget( request_btn );
setLayout( vlayout );
connect( request_btn, SIGNAL( released() ),
this, SLOT( request_btn_handler() ) );
/*
* Go to the next input field if user pressed Enter in the previous one
*/
connect( field_width, SIGNAL( returnPressed() ),
this, SLOT( go_to_field_height() ) );
connect( field_height, SIGNAL( returnPressed() ),
this, SLOT( go_to_field_x_pos() ) );
connect( field_x_pos, SIGNAL( returnPressed() ),
this, SLOT( go_to_field_y_pos() ) );
connect( field_y_pos, SIGNAL( returnPressed() ),
this, SLOT( go_to_field_scale() ) );
connect( field_scale, SIGNAL( returnPressed() ),
this, SLOT( go_to_request_btn() ) );
request_btn->setDefault( true );
/*
* Trying to imitate click
*/
if ( _consoleMode ) {
request_btn_handler();
}
}
void MainWidget::start_processing_request()
{
/*
* Latitudes and longtitudes may have "Intermediate" validation state,
* which means that if 180 is the greatest value that is acceptable,
* user can't type in 1800, but he can type in 184, which is out of range.
* This check prohibits invalid input.
*
* Width and height validation is omitted due to the fact that only
* positive numbers are allowed.
*
* See: https://doc.qt.io/qt-5/qintvalidator.html#validate
*/
int cursor = 0;
QString temp_x_pos = field_x_pos->text();
QString temp_y_pos = field_y_pos->text();
if ( longtitude_validator->validate( temp_x_pos, cursor ) != QValidator::Acceptable )
{
field_x_pos->clear();
return;
}
if ( latitude_validator->validate( temp_y_pos, cursor ) != QValidator::Acceptable )
{
field_y_pos->clear();
return;
}
/*
* Request map rendering from server and wait for reply.
*/
process_request();
}
void MainWidget::process_request()
{
double lon = field_x_pos->text().toDouble();
double lat = field_y_pos->text().toDouble();
uint w = field_width->text().toUInt();
uint h = field_height->text().toUInt();
uint scale = field_scale->text().toUInt();
QString format = formatBox->currentText();
QString url = urlEdit->text();
qDebug() << "Sending request: ";
qDebug() << "X:" << lon
<< "Y:" << lat
<< "Width:" << w
<< "Height:" << h
<< "Scale: 1 :" << scale
<< "Format:" << format;
uint orderId = 0;
char pinCode[128];
int status = gis_map_server_request_map( url.toStdString().c_str(), lat, lon, scale, w, h, format.toStdString().c_str(), &orderId, pinCode );
if ( status != 0 ) {
qDebug() << "Failed to make request" << status;
if ( _consoleMode ) {
exit(1);
}
return;
}
qDebug() << "Client got id:" << orderId;
qDebug() << "Client got pincode:" << pinCode;
status = gis_map_server_get_image( url.toStdString().c_str(), orderId, pinCode, &image );
if ( status != 0 ) {
qDebug() << "Failed to get image" << status;
if ( _consoleMode ) {
exit(1);
}
return;
}
qDebug() << "Got image";
if ( _consoleMode ) {
if ( image.save( _outputImageName ) )
qDebug() << "Image was saved to current directory as " << _outputImageName;
else
qDebug() << "Failed to save image as " << _outputImageName;
exit(0);
}
emit request_completed( image );
}
void MainWidget::set_UI_active( bool state )
{
field_x_pos->setEnabled( state );
field_y_pos->setEnabled( state );
field_width->setEnabled( state );
field_height->setEnabled( state );
field_scale->setEnabled( state );
}
void MainWidget::request_btn_handler()
{
set_UI_active( false );
start_processing_request();
set_UI_active( true );
}

View File

@ -0,0 +1,72 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QValidator>
#include <QWidget>
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget( double x_pos,
double y_pos,
uint32_t width,
uint32_t height,
uint32_t scale,
const char *server_url,
const char *image_format,
const char *outputImageName,
bool consoleMode );
QImage image;
private:
const bool _consoleMode;
const char *_outputImageName;
const char *_server_url;
const char *_image_format;
void process_request();
void set_UI_active( bool state );
bool request_btn_state;
QDoubleValidator *longtitude_validator;
QDoubleValidator *latitude_validator;
QLineEdit *field_x_pos;
QLineEdit *field_y_pos;
QLineEdit *field_width;
QLineEdit *field_height;
QLineEdit *field_scale;
QLineEdit *urlEdit;
QComboBox *formatBox;
QPushButton *request_btn;
signals:
void request_completed( const QImage &img );
void request_failed();
public slots:
void start_processing_request();
void request_btn_handler();
/*
* Go to the next input field if user pressed Enter in the previous one
*/
void go_to_field_height() { field_height->setFocus( Qt::OtherFocusReason ); }
void go_to_field_x_pos() { field_x_pos->setFocus( Qt::OtherFocusReason ); }
void go_to_field_y_pos() { field_y_pos->setFocus( Qt::OtherFocusReason ); }
void go_to_field_scale() { field_scale->setFocus( Qt::OtherFocusReason ); }
void go_to_request_btn() { request_btn->setFocus( Qt::OtherFocusReason ); }
};
#endif // MAINWIDGET_H

View File

@ -0,0 +1,63 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "mainwindow.h"
#include "mainwidget.h"
MainWindow::MainWindow( double x_pos,
double y_pos,
uint32_t width,
uint32_t height,
uint32_t scale,
const char *server_url,
const char *image_format,
const char *outputImageName,
bool consoleMode ) : QMainWindow()
{
/*
* Left dock menu panel widget with request parameters
*/
QDockWidget *dock_widget = new QDockWidget( tr( "Request parameters" ), this );
dock_widget->setAllowedAreas( Qt::LeftDockWidgetArea );
dock_widget->setFeatures( QDockWidget::NoDockWidgetFeatures );
MainWidget *menu = new MainWidget( x_pos,
y_pos,
width,
height,
scale,
server_url,
image_format,
outputImageName,
consoleMode );
dock_widget->setWidget( menu );
addDockWidget( Qt::LeftDockWidgetArea, dock_widget );
/*
* Resulting image viewport widget
*/
viewport = new ImageViewport();
viewport->show();
connect( menu, SIGNAL( request_completed( const QImage&) ),
viewport, SLOT( show_map_image( const QImage&) ) );
connect( menu, SIGNAL( request_failed() ),
viewport, SLOT( clean_map_image() ) );
setMinimumSize( 700, 400 );
setWindowTitle( tr( "Cartographic client" ) );
setCentralWidget( viewport );
}
/*
* Reimplementation of Qt's resizeEvent() for image fitting
* in the viewport after each change of the window's size.
*/
void MainWindow::resizeEvent( QResizeEvent *event )
{
Q_UNUSED( event );
viewport->fit_image();
}

View File

@ -0,0 +1,35 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QDockWidget>
#include <QMainWindow>
#include <QWheelEvent>
#include "image_viewport.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow( double x_pos,
double y_pos,
uint32_t width,
uint32_t height,
uint32_t scale,
const char *server_url,
const char *image_format,
const char *outputImageName,
bool consoleMode );
private:
ImageViewport *viewport;
protected:
virtual void resizeEvent( QResizeEvent *event );
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,156 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "mapsender.h"
#include <QApplication>
#include <QBuffer>
#include <QDateTime>
#include <QDebug>
#include <QImage>
#include "gismapserver.h"
int MapSender::sendRequest( double lat,
double lon,
int scale,
int w,
int h,
const char* format,
uint *orderId,
char *pinCode )
{
QString requestUrl = _url + "/?";
requestUrl += "&lat=" + QString::number(lat);
requestUrl += "&lon=" + QString::number(lon);
requestUrl += "&scale=" + QString::number(scale);
requestUrl += "&w=" + QString::number(w);
requestUrl += "&h=" + QString::number(h);
requestUrl += "&format=" + QString(format);
QUrl url( requestUrl );
QNetworkRequest request(url);
request.setRawHeader( QByteArray("agent"), QByteArray("gis") );
QNetworkReply *reply = manager->get(request);
waitForReplyFinished( reply, _timeout_ms );
QVariant statusCodeAttribute = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
bool ok = false;
GisHttpCodes statusCode = static_cast<GisHttpCodes>(statusCodeAttribute.toInt(&ok));
if ( !ok || statusCode != GisHttpCodes::Ok ) {
qDebug() << gis_map_server_get_error_message( statusCode );
return EFAULT;
}
QString orderId_tag = "orderId=";
QString pinCode_tag = "pincode=";
QByteArray rawData = reply->readAll();
QList<QByteArray> rawLines = rawData.split(',');
QString orderStr( rawLines[0] );
if (orderStr.contains( orderId_tag )) {
orderStr.remove(orderId_tag);
*orderId = orderStr.toUInt( &ok );
if ( !ok ) {
qDebug() << "Invalid <orderId>";
return EFAULT;
}
}
QString pinCodeStr( rawLines[1] );
if (pinCodeStr.contains( pinCode_tag )) {
pinCodeStr.remove(pinCode_tag);
pinCodeStr.remove( " " );
strcpy( pinCode, pinCodeStr.toStdString().c_str() );
}
return 0;
}
int MapSender::getImageWithWaiting( char *pinCode, QImage *image )
{
uint waitMs = 100;
QDateTime start = QDateTime::currentDateTimeUtc();
QDateTime prevRequestTime = QDateTime::currentDateTimeUtc();
QDateTime currentRequestTime = prevRequestTime;
int status = getImage( pinCode, image );
while ( status == EAGAIN ) {
qApp->processEvents( QEventLoop::AllEvents, waitMs );
currentRequestTime = QDateTime::currentDateTimeUtc();
qint64 requestdeltaMs = prevRequestTime.msecsTo( currentRequestTime );
if ( requestdeltaMs > 500 ) {
prevRequestTime = currentRequestTime;
status = getImage( pinCode, image );
}
QDateTime current = QDateTime::currentDateTimeUtc();
auto totalDeltaMs = start.msecsTo( current );
if ( totalDeltaMs > _timeout_ms )
return ETIMEDOUT;
}
return status;
}
int MapSender::getImage( char *pinCode, QImage *image )
{
QString requestUrl = _url + "/?&orderId=" + QString::number(_orderId) + "&pincode=" + QString(pinCode);
QUrl url( requestUrl );
QNetworkRequest request(url);
QNetworkReply *reply = manager->get(request);
waitForReplyFinished( reply, _timeout_ms );
QVariant statusCodeAttribute = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
bool ok = false;
int statusCode = statusCodeAttribute.toInt(&ok);
if ( !ok || statusCode != GisHttpCodes::Ok ) {
if ( statusCode == GisHttpCodes::IsProcessing ) {
return EAGAIN;
}
qDebug() << "statusCode" << statusCode;
return EFAULT;
}
if ( !reply->isFinished() )
return EFAULT;
QVariant format_hdr = reply->header( QNetworkRequest::ContentTypeHeader );
QString format_str = format_hdr.toString();
QString image_format = format_str.remove( "image/" );
QByteArray ba = reply->readAll();
image->loadFromData( ba, image_format.toLocal8Bit().data() );
return 0;
}
void MapSender::waitForReplyFinished( QNetworkReply *reply, uint timeout_ms )
{
uint waitMs = 100;
bool isTimeoutEnabled = false;
if ( timeout_ms > 0 )
isTimeoutEnabled = true;
QDateTime start = QDateTime::currentDateTimeUtc();
qint64 totalDeltaMs = 0;
while ( !reply->isFinished() ) {
qApp->processEvents( QEventLoop::AllEvents, waitMs );
if ( isTimeoutEnabled ) {
QDateTime current = QDateTime::currentDateTimeUtc();
totalDeltaMs = start.msecsTo( current );
if ( totalDeltaMs > timeout_ms )
{
qDebug() << "Timeout" << totalDeltaMs;
break;
}
}
}
}

View File

@ -0,0 +1,37 @@
/*
* (c) 2011-2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef MAPSENDER_H
#define MAPSENDER_H
#include <QNetworkReply>
#include <QNetworkAccessManager>
class MapSender : public QObject
{
Q_OBJECT
public:
explicit MapSender( const QString &server_address, const int userId, const uint timeout_ms ) :
_url( server_address ),
_orderId( userId ),
_timeout_ms( timeout_ms )
{
manager = new QNetworkAccessManager();
}
//Client calls
int sendRequest(double lat, double lon, int scale, int w, int h, const char* format, uint* userId , char* pinCode);
int getImageWithWaiting( char *pinCode, QImage *image );
private:
int getImage( char *pinCode, QImage *image );
void waitForReplyFinished( QNetworkReply *reply, uint timeout_ms );
const QString _url;
const int _orderId;
const uint _timeout_ms;
QNetworkAccessManager *manager;
};
#endif // MAPSENDER_H

View File

@ -0,0 +1,8 @@
LIST=CPU
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>translations/gis-client-render_ru.qm</file>
</qresource>
</RCC>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="ru_RU">
<context>
<name>MainWidget</name>
<message>
<location filename="../mainwidget.cpp" line="12"/>
<source>Longtitude:</source>
<translation>Долгота:</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="13"/>
<source>Latitude:</source>
<translation>Широта:</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="14"/>
<source>Image width (px):</source>
<translation>Ширина изображения в пикс.</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="15"/>
<source>Image height (px):</source>
<translation>Высота изображения в пикс.</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="16"/>
<source>Scale:</source>
<translation>Масштаб:</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="23"/>
<location filename="../mainwidget.cpp" line="204"/>
<source>Request map</source>
<translation>Выполнить запрос</translation>
</message>
<message>
<location filename="../mainwidget.cpp" line="206"/>
<source>Abort request</source>
<translation>Прервать запрос</translation>
</message>
</context>
<context>
<name>MainWindow</name>
<message>
<location filename="../mainwindow.cpp" line="31"/>
<source>Cartographic client</source>
<translation>Картографический клиент</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="12"/>
<source>Request parameters</source>
<translation>Параметры запроса</translation>
</message>
</context>
</TS>

View File

@ -0,0 +1,2 @@
LIST=OS
include recurse.mk

View File

@ -0,0 +1,6 @@
## gis-map-server - карт-сервер, предназначенный для работы с ПК ЦКИ
<br/>
<br/>
![GIS Map Server IPC](docs/gis-map-server_IPC.drawio.png)

15
src/gis-map-server/START.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
if [ -z "$GIS_ROOT" ]; then
export GIS_ROOT=/opt/gis/
fi
export LD_LIBRARY_PATH=$GIS_ROOT/lib:$LD_LIBRARY_PATH
export PATH=$GIS_ROOT/bin:$GIS_ROOT/sbin:$PATH
export GIS_DEBUG_LEVEL=2
export GIS_DISABLE_VERIFIER=y
slay -f gis-core
gis-core -dsxf-local,sync=soft -ds57-local,sync=soft &
waitfor /dev/gis_core 300
echo 'Starting server'
$GIS_ROOT/data/resources/gis-map-server/server.py $GIS_ROOT/data/config/gis-map-server.conf

View File

@ -0,0 +1,20 @@
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
include $(MKFILES_ROOT)/qmacros.mk
NAME=$(notdir ${PROJECT_ROOT})
INSTALLDIR=/opt/gis/sbin/
RESOURCESDIR=/opt/gis/data/resources/${NAME}
LOGDIR=/opt/gis/data/config
PRE_INSTALL=$(CP_HOST) ${PROJECT_ROOT}/START.sh ${INSTALL_ROOT_nto}/${CPUVARDIR}/${INSTALLDIR}/${NAME}; $(CP_HOST) ${PROJECT_ROOT}/*.py ${INSTALL_ROOT_nto}/${CPUVARDIR}/${RESOURCESDIR}/
POST_INSTALL=$(CP_HOST) -R ${PROJECT_ROOT}/html ${INSTALL_ROOT_nto}/${CPUVARDIR}/${RESOURCESDIR}; $(CP_HOST) ${PROJECT_ROOT}/$(NAME).conf ${INSTALL_ROOT_nto}/${CPUVARDIR}/${LOGDIR}/$(NAME).conf
ALL_DEPENDENCIES=Makefile
include $(MKFILES_ROOT)/qtargets.mk

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2021-12-21T13:21:52.932Z" agent="5.0 (X11)" etag="9BBCW-6gea-xDeGxZfHn" version="16.0.0" type="device"><diagram id="b_8fcSy7Od5qwKq7muho" name="Page-1">7Vhbb5swFP41PG7iEiA8hqTdHjatWnd9qhxwwZvBkXGadL9+tjHGBtpcVLXVNAkhczgc4+/77HNsJ1hW+3cUbMqPJIfY8d187wQrx/e9me874nLz+9Yyd5WhoChXTr3hGv2Byugq6xblsLEcGSGYoY1tzEhdw4xZNkAp2dlutwTbvW5AAUeG6wzgsfU7ylmpRuHHvf09REXZ9exFSfumAp2zGklTgpzsDFNw4QRLSghrW9V+CbEAr8Ol/e7ygbf6xyis2TEffLnJPq9XYEHd5nLbXCf4J/rxRkW5A3irBuysXCdZiHuayvtc3kN5Xzkrz5n78p6qYbH7Dqs7SBni0H0Aa4ivSIMYIjV/tSaMkcoJ0s5hgVEhXjCy4daSVZg/eLzJMdqIYNW+EHJ6uwYNyt5icA8pzG+oJDiV4uL6CFKyZRjVcKm5F0Y1It4V3D8IlacJ4MqFpIKM3nOXfSfS9gulWS8O2+ddrwDPVT6lwX6kbECJrtCRe154Q1FzAk3+mCY/whIMdMebhWgazLmSIbdji1su5D3o7Npn1nHM23HHNG97sr0wFJB0XfIRmL1O/0h62XWiw4Tqd9QHazoMMQz8r8orenX6Cib0NUAf1vlCrKf8qSY1FECBpoS5gtZAWdivAGOQ1tLiu4HAnlHyW6+hHJ2Ukm2dywA9rjAfLccHUTVQCydA62wUYsDQnR1+CknVwxVBvGNNmj+zSQviARkN2dIMqq/MdfhAoCGpDNACslEcyase9flUz/5TfYjqaER1eB7Vw0BR+KxUh4eptonZlYjB6w3IxNsdXyLHbGsVjEneQIr4L0MqIqC6UI5PsGCOEnK3OBp6CKYWTF1GPvmKGR2bkReelfz6WsrMyzy3usfnxemEO7crNN2nmedD1Vub4Y9Lu71EvMMSGaviKehPYpv+rvo286X/nPkyHrMvqA7t8qqlQ1OTGqVWouh4fWDrvKbA1vuXFwN7fuxUS5b2VkW3W/FH1qQ4aW6ZVbJnlLehsR+K7TmnaY+Najsy7POzC+GXF8lsFjzbjPzaQPpp/UtU4L6LRe1vzDhvBLq99LXjwhycku/8IbVPA2pQKUi/AYrAmgMs7Y/KMTGQUfI1uOGYsikClgQT2hdUtwjjgQmoXUvGOYB0YjtToTwX3UwyTrj3LZY1W8n9YP1Eq0EyXawaPM+n8u7pNPNHg+lTjjDcw2WOUdFmGDR81yeJAZSNzQZ155WtvGdZHz72z61fW/8dquBebMfjDTJBeO6OJxoEcp93y+NNnHK9doWERyrEf0mFDA8yguHyfqxC4mAQaFi3n60Q/tgfsbbu/UF1cPEX</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View File

@ -0,0 +1,6 @@
SERVER_ADDRESS=localhost
SERVER_PORT=8000
SLOTS_NUMBER=2
STORAGE_MAX_SIZE=6400000
HTML_PAGES_PATH=./data/resources/gis-map-server/html/
GIS_SHID=770

View File

@ -0,0 +1,14 @@
<html>
<head>
<title></title>
<meta content="">
<style></style>
</head>
<body>
<center>
<h2>Order is accepted</h2>
<p>Order id = ORDERID, pincode = PIN_CODE</p>
<a href='http://ADDRESS:PORT/?orderId=ORDERID&pincode=PIN_CODE'> Click here to obtain your order </a>
</center>
</body>
</html>

View File

@ -0,0 +1,165 @@
<html>
<head>
<title></title>
<meta content="">
<style>
.params_frame {
width: 200px;
padding: 10px;
float: left;
border: 1px solid black;
margin: 7px;
}
.results_frame {
width: calc(100% - 288px);
height: calc(100vh - 58px );
float: left;
border: 1px solid black;
margin: 7px;
overflow: scroll;
}
<