This commit is contained in:
Vadim Likholetov 2024-01-18 09:50:00 +02:00
commit 11f85b1975
9 changed files with 455 additions and 0 deletions

21
.idea/YASARMapper.iml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="Flask">
<option name="enabled" value="true" />
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/../YASARMapper\templates" />
</list>
</option>
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 (YASARMapper)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (YASARMapper)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/YASARMapper.iml" filepath="$PROJECT_DIR$/.idea/YASARMapper.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

126
.idea/workspace.xml Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="fab2b692-df07-4475-9920-89184af9570f" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.idea/YASARMapper.iml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
<change afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/map.html" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Flask Main" />
<option value="HTML File" />
</list>
</option>
</component>
<component name="FlaskConsoleOptions" custom-start-script="import sys&#10;sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo&#10;locals().update(ScriptInfo(create_app=None).load_app().make_shell_context())&#10;print(&quot;Python %s on %s\nApp: %s [%s]\nInstance: %s&quot; % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))">
<envs>
<env key="FLASK_APP" value="app" />
</envs>
<option name="myCustomStartScript" value="import sys&#10;sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo&#10;locals().update(ScriptInfo(create_app=None).load_app().make_shell_context())&#10;print(&quot;Python %s on %s\nApp: %s [%s]\nInstance: %s&quot; % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))" />
<option name="myEnvs">
<map>
<entry key="FLASK_APP" value="app" />
</map>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 6
}</component>
<component name="ProjectId" id="2apCCQY086bQUzCQRTZuOcw5dYT" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
&quot;Flask server.YASARMapper.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</component>
<component name="RunManager">
<configuration name="YASARMapper" type="Python.FlaskServer">
<module name="YASARMapper" />
<option name="target" value="$PROJECT_DIR$/app.py" />
<option name="targetType" value="PATH" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="GOOGLE_API_KEY" value="AIzaSyB7eRVH82_8CjtTirgt8x1jeP4Bqvf5IOg" />
</envs>
<option name="SDK_HOME" value="D:\yasarmapper\YASARMapper\.venv\Scripts\python.exe" />
<option name="SDK_NAME" value="Python 3.11 (YASARMapper)" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="launchJavascriptDebuger" value="false" />
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-python-sdk-50da183f06c8-2887949eec09-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-233.13135.95" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="fab2b692-df07-4475-9920-89184af9570f" name="Changes" comment="" />
<created>1705006008005</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1705006008005</updated>
<workItem from="1705006014810" duration="6336000" />
<workItem from="1705152473218" duration="109000" />
<workItem from="1705152597513" duration="1277000" />
<workItem from="1705428459903" duration="4231000" />
<workItem from="1705482341049" duration="50000" />
<workItem from="1705482409485" duration="7760000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/YASARMapper$YASARMapper.coverage" NAME="YASARMapper Coverage Results" MODIFIED="1705520887309" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
</component>
</project>

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
# Use an official Python runtime as a parent image
FROM python:3.8-slim-buster
# Set the working directory in the container to /app
WORKDIR /app
# Add the current directory contents into the container at /app
ADD . /app
# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Run app.py when the container launches
CMD ["python", "app.py"]

25
app.py Normal file
View File

@ -0,0 +1,25 @@
import os
from flask import Flask, render_template, jsonify
app = Flask(__name__)
@app.route('/api_key', methods=['GET'])
def api_key():
return jsonify({"apiKey": os.getenv("GOOGLE_API_KEY")})
@app.route('/save_shape', methods=['POST'])
def save_shape():
shape_data = request.json
# Process shape_data as needed
return jsonify({"status": "success"})
@app.route('/')
def home():
return render_template('map.html')
if __name__ == '__main__':
app.run()

239
templates/map.html Normal file
View File

@ -0,0 +1,239 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Fullscreen Map</title>
<style>
body, html {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="https://unpkg.com/osmtogeojson"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-geometryutil/0.9.0/leaflet.geometryutil.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<link rel="stylesheet" href="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.css"/>
<script src="https://ppete2.github.io/Leaflet.PolylineMeasure/Leaflet.PolylineMeasure.js"></script>
<!-- Include the Leaflet Panel Layers plugin -->
<link rel="stylesheet" href="https://opengeo.tech/maps/leaflet-panel-layers/dist/leaflet-panel-layers.src.css"/>
<script src="https://opengeo.tech/maps/leaflet-panel-layers/dist/leaflet-panel-layers.src.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"/>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
</head>
<body>
<button id="select-linear-features">Linear Features</button>
<div id="map"></div>
<script>
function addMeasurementLabel(layer, text) {
var label = new L.Label();
label.setContent(text);
label.setLatLng(layer.getBounds().getCenter());
map.showLabel(label);
}
//////////////////////////////////////////
if (typeof osmtogeojson !== 'function') {
console.error('osmtogeojson is not loaded properly');
} else {
console.log('osmtogeojson is loaded properly');
}
var streets = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors',
});
var satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 19,
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
});
var fintopo = L.tileLayer('https://wmts.mapant.fi/wmts_EPSG3857.php?z={z}&x={x}&y={y}', {
maxZoom: 19,
attribution: ''
});
var strava = L.tileLayer('https://proxy.nakarte.me/https/heatmap-external-a.strava.com/tiles-auth/all/purple/{z}/{x}/{y}.png?px=256', {
maxZoom: 19,
attribution: ''
});
var map = L.map('map', {
center: [61.49, 23.76],
zoom: 13,
layers: [streets] //default layer
});
// Define the base layers using the plugin's syntax
var baseLayers = [
{
name: "OSM",
layer: streets
},
{
name: "ERSI Sattelite",
layer: satellite
},
{
name: "Suomi Topo",
layer: fintopo
}
];
var overLayers = [
{
name: "Strava",
layer: strava
}
];
// Add the panel layers control to the map
var panelLayers = new L.Control.PanelLayers(baseLayers, overLayers, {
collapsibleGroups: true,
collapsed: true,
position: 'topright'
});
map.addControl(panelLayers);
// Adding geocoder
$.ajax({
url: '/api_key',
type: 'GET',
success: function (response) {
var geocoder = L.Control.geocoder({
geocoder: L.Control.Geocoder.google({apiKey: response.apiKey}),
position: 'topleft'
}).addTo(map);
}
});
// Adding scale
L.control.scale({metric: true, imperial: false}).addTo(map); //Adding scale
// Initialize the FeatureGroup to store editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Initialize the draw control and pass it the FeatureGroup of editable layers
var drawControl = new L.Control.Draw({
draw: {
polyline: {
metric: true, // Use metric measurements
showLength: true // Show length of the polyline
},
polygon: {
showArea: true, // Show area of the polygon
metric: true
},
circle: {
metric: true,
showRadius: true // Show radius for circles
},
rectangle: false,
circlemarker: false
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
var shape = layer.toGeoJSON();
var shape_for_db = JSON.stringify(shape);
var measurementText;
if (event.layerType === 'polygon') {
var area = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]);
measurementText = "Area: " + area.toFixed(2) + " sqm";
} else if (event.layerType === 'polyline') {
var length = L.GeometryUtil.length(layer);
measurementText = "Length: " + length.toFixed(2) + " m";
} else if (event.layerType === 'circle') {
var radius = layer.getRadius();
var area = Math.PI * radius * radius;
measurementText = "Area: " + area.toFixed(2) + " sqm";
}
if (measurementText) {
addMeasurementLabel(layer, measurementText);
}
// Example AJAX call to send data to Flask
$.ajax({
type: "POST",
url: "/save_shape",
data: shape_for_db,
contentType: "application/json",
dataType: "json",
success: function (response) {
console.log(response);
}
});
});
// Adding polyline measure control
L.control.polylineMeasure({
position: 'topleft',
unit: 'metres',
showBearings: true,
clearMeasurementsOnStop: false,
showClearControl: true,
showUnitControl: false
}).addTo(map);
// Adding linear features
document.getElementById('select-linear-features').addEventListener('click', function () {
var overpassUrl = "https://overpass-api.de/api/interpreter";
var query = `
[out:json];
(
way["highway"](around:5000,61.49,23.76);
way["waterway"](around:5000,61.49,23.76);
);
out body;
>;
out skel qt;
`;
$.ajax({
url: overpassUrl,
type: 'GET',
data: {
data: query
},
success: function (response) {
var geojson = osmtogeojson(response);
L.geoJSON(geojson).addTo(map);
}
});
});
</script>
</body>
</html>