Merge pull request #89 from louis-e/gui-impl

Implemented GUI
This commit is contained in:
Louis Erbkamm
2024-12-25 00:52:01 +01:00
committed by GitHub
96 changed files with 49420 additions and 296 deletions

1
.envrc
View File

@@ -1 +0,0 @@
use flake ./flake/flake.nix

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [windows-latest, macos-latest]
steps:
- name: Checkout code
@@ -33,7 +33,7 @@ jobs:
- name: Check clippy lints
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Install dependencies
- name: Install Rust dependencies
run: cargo fetch
- name: Build

50
.gitignore vendored
View File

@@ -1,5 +1,51 @@
# Environment files
.env
.envrc
/.direnv
# Build artifacts
/target
**/*.rs.bk
# Lock files
Cargo.lock
export.json
parsed_osm_data.txt
# IDE/editor files
.idea/
/.vscode/
/*.swp
*.iml
*.suo
*.ntvs*
*.njsproj
*.sln
*.ps1
# System files
.DS_Store
Thumbs.db
*.tmp
*.log
# Generated files
/export.json
/parsed_osm_data.txt
/gen/
/build/
*.rmeta
*.dSYM
# Tauri specific
flake/
capabilities/
gen/
# Miscellaneous
*.bak
*.old
*.orig
# Ignore all in flake directory except specific files
/flake/*
!/flake/flake.nix
!/flake/flake.lock

View File

@@ -1,6 +1,6 @@
[package]
name = "arnis"
version = "2.0.0"
version = "2.1.0"
edition = "2021"
description = "Arnis - Generate real life cities in Minecraft"
homepage = "https://github.com/louis-e/arnis"
@@ -11,9 +11,13 @@ readme = "README.md"
[profile.release]
lto = true
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
clap = { version = "4.1", features = ["derive"] }
colored = "2.1.0"
dirs = "4.0.0"
fastanvil = "0.31.0"
fastnbt = "2.5.0"
fnv = "1.0.7"
@@ -24,6 +28,11 @@ nalgebra = "0.33.0"
once_cell = "1.19.0"
rand = "0.8.5"
reqwest = { version = "0.12.7", features = ["blocking", "json"] }
rfd = "0.15.0"
semver = "1.0.23"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "2", features = [] }
tauri-plugin-dialog = "2.0.0"
tauri-plugin-shell = "2"
tokio = { version = "1.42.0", features = ["full"] }

View File

@@ -2,7 +2,7 @@
<img width="456" height="125" src="https://github.com/louis-e/arnis/blob/main/gitassets/logo.png?raw=true">
</p>
# Arnis [![CI Build Status](https://github.com/louis-e/arnis/actions/workflows/ci-build.yml/badge.svg)](https://github.com/louis-e/arnis/actions)
# Arnis [![CI Build Status](https://github.com/louis-e/arnis/actions/workflows/ci-build.yml/badge.svg)](https://github.com/louis-e/arnis/actions) [<img alt="GitHub Release" src="https://img.shields.io/github/v/release/louis-e/arnis" />](https://github.com/louis-e/arnis/releases) [<img alt="GitHub Downloads (all assets, all releases" src="https://img.shields.io/github/downloads/louis-e/arnis/total" />](https://github.com/louis-e/arnis/releases)
This open source project written in Rust generates any chosen location from the real world in Minecraft Java Edition with a high level of detail.
@@ -13,6 +13,28 @@ By leveraging geospatial data from OpenStreetMap and utilizing the powerful capa
Arnis is designed to handle large-scale data and generate rich, immersive environments that bring real-world cities, landmarks, and natural features into the Minecraft universe. Whether you're looking to replicate your hometown, explore urban environments, or simply build something unique and realistic, Arnis offers a comprehensive toolset to achieve your vision.
## :keyboard: Usage
<img width="60%" src="https://github.com/louis-e/arnis/blob/main/gitassets/gui.png?raw=true"><br>
Download the [latest release](https://github.com/louis-e/arnis/releases/) or [compile](#trophy-open-source) the project on your own.
Make sure to generate a new flat world in advance in Minecraft. Then choose your area in Arnis using the rectangle tool and select your Minecraft world - then simply click on 'Start Generation'!
The world will always be generated starting from the coordinates 0 0 0.
<details>
<summary>Alternatively you can also run Arnis the old fashioned way in the command line.</summary>
```arnis.exe --path="C:/YOUR_PATH/.minecraft/saves/worldname" --bbox="min_lng,min_lat,max_lng,max_lat"```
The --bbox parameter specifies the bounding box coordinates in the format: min_lng,min_lat,max_lng,max_lat. Use --path to specify the location of the Minecraft world.
<img width="60%" src="https://github.com/louis-e/arnis/blob/main/gitassets/bbox-finder.png?raw=true"><br>
Use http://bboxfinder.com/ to draw a rectangle of your wanted area. Then copy the four box coordinates as shown below and use them as the input for the --bbox parameter. Try starting with a small area since large areas take a lot of computing power and time to process.<br>
<i>Note: This might not be working right now since the console gets suppressed.</i>
</details>
## :floppy_disk: How it works
![CLI Generation](https://github.com/louis-e/arnis/blob/main/gitassets/cli.gif?raw=true)
@@ -26,19 +48,6 @@ The raw data obtained from the API *[(see FAQ)](#question-faq)* includes each el
5. Generate Ground Layer: A ground layer is generated based on the provided scale factors to provide a base for the entire Minecraft world. This step ensures all areas have an appropriate foundation (e.g., grass and dirt layers).
6. Save the Minecraft World: All the modified chunks are saved back to the Minecraft region files.
## :keyboard: Usage
Get the [latest release](https://github.com/louis-e/arnis/releases/) or [compile](#trophy-open-source) the project on your own.
#### Run: ```arnis.exe --path="C:/YOUR_PATH/.minecraft/saves/worldname" --bbox="min_lng,min_lat,max_lng,max_lat"```
### How to find your bbox coordinates
Use http://bboxfinder.com/ to draw a rectangle of your wanted area. Then copy the four box coordinates as shown below and use them as the input for the --bbox parameter.
![How to find area](https://github.com/louis-e/arnis/blob/main/gitassets/bbox-finder.png?raw=true)
The world will always be generated starting from the coordinates 0 0 0. Try starting with a small area since large areas take a lot of computing power and time to process.
Manually generate a new Minecraft world (preferably a flat world) before running the script.
The --bbox parameter specifies the bounding box coordinates in the format: min_lng,min_lat,max_lng,max_lat.
Use --path to specify the location of the Minecraft world.
## :question: FAQ
- *Wasn't this written in Python before?*<br>
Yes! Arnis was initially developed in Python, which benefited from Python's open-source friendliness and ease of readability. This is why we strive for clear, well-documented code in the Rust port of this project to find the right balance. I decided to port the project to Rust to learn more about it and push the algorithm's performance further. We were nearing the limits of optimization in Python, and Rust's capabilities allow for even better performance and efficiency. The old Python implementation is still available in the python-legacy branch.
@@ -51,7 +60,6 @@ The project is named after the smallest city in Germany, Arnis[^2]. The city's s
## :memo: ToDo and Known Bugs
Feel free to choose an item from the To-Do or Known Bugs list, or bring your own idea to the table. Bug reports shall be raised as a Github issue. Contributions are highly welcome and appreciated!
- [ ] Design and implement a GUI
- [ ] Memory optimization
- [ ] Fix Github Action Workflow for releasing Linux & MacOS Binary
- [ ] Evaluate and implement multithreaded region saving
@@ -67,6 +75,7 @@ Feel free to choose an item from the To-Do or Known Bugs list, or bring your own
- [ ] Evaluate and implement elevation
- [ ] Generate a few big cities using high performance hardware and make them available to download
- [ ] Implement memory mapped storing of chunks to reduce memory usage
- [x] Design and implement a GUI
- [x] Fix faulty empty chunks ([https://github.com/owengage/fastnbt/issues/120](https://github.com/owengage/fastnbt/issues/120)) (workaround found)
## :trophy: Open Source
@@ -81,6 +90,7 @@ Feel free to choose an item from the To-Do or Known Bugs list, or bring your own
This project is open source and welcomes contributions from everyone! Whether you're interested in fixing bugs, improving performance, adding new features, or enhancing documentation, your input is valuable. Simply fork the repository, make your changes, and submit a pull request. We encourage discussions and suggestions to ensure the project remains modular, optimized, and easy to use for the community. You can use the parameter --debug to get a more detailed output of the processed values, which can be helpful for debugging and development. Contributions of all levels are appreciated, and your efforts help improve this tool for everyone.
Build and run it using: ```cargo run --release -- --path="C:/YOUR_PATH/.minecraft/saves/worldname" --bbox="min_lng,min_lat,max_lng,max_lat"```<br>
For the GUI: ```cargo run --release```<br>
After your pull request was merged, I will take care of regularly creating update releases which will include your changes.

3
build.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

347
gui-src/css/bbox.css Normal file
View File

@@ -0,0 +1,347 @@
html,
body,
#map {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
font-family: "Courier New", Courier, monospace;
}
#info-box {
position: absolute;
width: 100%;
height: auto;
bottom: 0;
border: 0 0 7px 0;
z-index: 10000;
}
#coord-format {
position: absolute;
bottom: 0;
right: 15;
}
#coord-format .bboxlabel {
width: 100%;
}
#coord-format form {
margin-bottom: 10px;
}
#info img {
vertical-align: middle;
height: 16px;
width: auto;
opacity: 0.6;
}
#info img:hover {
opacity: 1;
cursor: pointer;
}
#info-toggle ul {
margin: 0 0 0 20px;
padding: 0;
}
#info-toggle ul li {
display: inline-block;
padding: 4px 7px 0;
background-color: rgba(230, 230, 230, 0.4);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
font-weight: 900;
cursor: pointer;
}
#info-toggle ul li.active {
background-color: rgba(255, 255, 255, 0.8);
}
#info-toggle-button {
background-color: rgba(0, 0, 0, 0.7) !important;
color: #fff;
}
#info-toggle-button:hover {
background-color: #f4f4f4;
}
#projlabel,
#wgslabel {
display: inline-block;
color: #333;
z-index: 10000;
}
#projcoords {
display: none;
}
.coords {
text-align: left;
padding: 7px 0;
background-color: rgba(255, 255, 255, 0.8);
}
#bbounds,
#mbounds,
#mcenter {
font-size: small;
display: block;
z-index: 10000;
padding: 2px 0;
}
#mouse,
#tile,
#zoom {
display: inline-block;
}
.bboxlabel {
font-size: small;
font-weight: bold;
z-index: 10000;
background-color: black;
color: white;
text-align: center;
display: inline-block;
padding-left: 2px;
width: 60px;
}
.bboxllpos,
.bboxllpossmall,
.bboxprojpos,
.bboxprojpossmall,
.tilesmall,
.zoomsmall {
font-size: small;
font-weight: bold;
color: black;
display: inline-block;
padding-left: 5px;
}
.bboxllpos {
width: 350px;
}
.bboxllpossmall {
width: 200px;
}
.bboxprojpos {
width: 450px;
}
.bboxprojpossmall {
width: 250px;
}
.tilesmall {
width: 60px;
}
.zoomsmall {
width: 20px;
}
#map-ui-proj {
position: absolute;
top: 460px;
left: 10px;
list-style: none;
margin: 0;
padding: 0;
z-index: 100;
}
#map-ui-proj a {
font: normal 13px/18px 'Helvetica Neue', Helvetica, sans-serif;
background: #FFF;
color: #3C4E5A;
display: block;
margin: 0;
padding: 0;
border: 1px solid #BBB;
border-bottom-width: 0;
min-width: 75px;
padding: 2px;
text-decoration: none;
text-align: center;
}
#map-ui-proj a:hover {
background: #ECF5FA;
}
#map-ui-proj li:last-child a {
border-bottom-width: 1px;
-webkit-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
}
#map-ui-proj li:first-child a {
-webkit-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
#map-ui-proj a.active {
background: #3887BE;
border-color: #3887BE;
border-top-color: #FFF;
color: #FFF;
}
.epsglabel {
font-size: small;
font-weight: bold;
background-color: black;
color: white;
z-index: 10000;
}
#map-ui {
position: absolute;
top: 330px;
left: 12px;
list-style: none;
margin: 0;
padding: 0;
z-index: 100;
}
#map-ui a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 30px;
height: 30px;
line-height: 30px;
display: block;
text-align: center;
text-decoration: none;
color: black;
background-image: url('css/maps/images/bbox-sprites.png');
background-repeat: no-repeat;
}
#map-ui a:hover {
background-color: #ECF5FA;
}
#map-ui li:last-child a {
border-bottom-width: 1px;
-webkit-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
background-position: -33px -2px;
}
#map-ui li:last-child a.enabled {
border-bottom-width: 1px;
-webkit-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
background-position: -278px -2px;
}
#map-ui li:first-child a {
-webkit-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
#create-geojson a.enabled {
background-position: -248px -2px;
}
#create-geojson a {
background-position: -2px -2px;
}
#geolocation a {
background-position: -186px -2px;
}
#geolocation a.active {
background-position: -63px -2px;
}
#help a.enabled {
background-position: -342px -2px;
}
#help a {
background-position: -93px -2px;
}
.zeroclipboard-is-hover {
opacity: 1 !important;
}
.leaflet-sidebar textarea {
/* display: none; */
height: 95%;
width: 100%;
border: 1;
padding: 2em;
resize: none;
-webkit-box-sizing: border-box;
/* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box;
/* Firefox, other Gecko */
box-sizing: border-box;
/* Opera/IE 8+ */
}
.leaflet-sidebar button {
font-size: large;
}
.leaflet-sidebar .close {
z-index: 10001;
}
.ui-autocomplete {
max-height: 180px;
overflow-y: auto;
/* prevent horizontal scrollbar */
overflow-x: hidden;
}
/* IE 6 doesn't support max-height
* we use height instead, but this forces the menu to always be this tall
*/
* html .ui-autocomplete {
height: 180px;
}
.unblurred.leaflet-tile-loaded {
-moz-filter: blur(0) sepia(0) invert(0);
-webkit-filter: blur(0) sepia(0) invert(0);
filter: blur(0) sepia(0) invert(0);
-moz-transition: all 1s ease;
-webkit-transition: all 1s ease;
transition: all 1s ease;
}
.blurred.leaflet-tile-loaded {
-moz-filter: blur(1px) sepia(1) invert(1);
-moz-transition: all 1s ease;
-webkit-filter: blur(1px) sepia(1) invert(1);
-webkit-transition: all 1s ease;
filter: blur(1px) sepia(1) invert(1);
transition: all 1s ease;
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,505 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="740"
height="60"
viewBox="0 0 740.00003 59.999999"
enable-background="new 0 0 100 100"
xml:space="preserve"
inkscape:version="0.48.2 r9819"
sodipodi:docname="bbox-sprites.svg"
inkscape:export-filename="/Users/aaronr/github/bboxfinder.com/css/maps/images/bbox-sprites.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45"><metadata
id="metadata51"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs49"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 50 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="100 : 50 : 1"
inkscape:persp3d-origin="50 : 33.333333 : 1"
id="perspective5373" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1226"
inkscape:window-height="844"
id="namedview47"
showgrid="true"
inkscape:zoom="1.32"
inkscape:cx="372.13373"
inkscape:cy="27.069218"
inkscape:window-x="8"
inkscape:window-y="1"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
borderlayer="false"><inkscape:grid
type="xygrid"
id="grid6480"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
<g
id="g3285"
transform="matrix(0.84510875,0,0,0.84510875,2.4666349,3.9025275)"><path
inkscape:connector-curvature="0"
id="path3147"
d="m 19.02918,27.70898 0,-1.42397 1.52567,0 1.52569,0 0,-4.78047 0,-4.78046 -1.6274,0 -1.62738,0 0,1.52568 0,1.52568 -1.52567,0 -1.52569,0 0,-3.05136 0,-3.05135 7.83182,0 7.83182,0 0,3.05135 0,3.05136 -1.52569,0 -1.52567,0 0,-1.52568 0,-1.52568 -1.62739,0 -1.62739,0 0,4.78046 0,4.78047 1.52568,0 1.52567,0 0,1.42397 0,1.42396 -4.57703,0 -4.57704,0 0,-1.42396 z"
style="fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path3149"
d="m 19.64356,39.48975 0,-5.51027 1.35498,0 1.35497,0 0,4.15529 0,4.15527 12.64649,0 12.6465,0 0,-9.8462 0,-9.84619 -5.60059,0 -5.60059,0 0,-1.26465 0,-1.26465 6.95558,0 6.95556,0 0,12.46582 0,12.46583 L 35,45 l -15.35644,0 0,-5.51025 z"
style="fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /></g><path
inkscape:connector-curvature="0"
id="path6172"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6178"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6184"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6186"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6270"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6288"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6682"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6684"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><g
id="g3192"
transform="matrix(0.27873303,0,0,0.27873303,128.9627,14.868054)"
style="fill:#464646;fill-opacity:1">
<g
id="g3194"
style="fill:#464646;fill-opacity:1">
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-114.388"
y="10.951"
clip-rule="evenodd"
width="20.32"
height="20.318001"
id="rect3196" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-114.388"
y="36.349998"
clip-rule="evenodd"
width="20.32"
height="20.318001"
id="rect3198" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-114.388"
y="61.748001"
clip-rule="evenodd"
width="20.32"
height="20.319"
id="rect3200" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-139.785"
y="10.951"
clip-rule="evenodd"
width="20.318001"
height="20.318001"
id="rect3202" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-139.785"
y="36.349998"
clip-rule="evenodd"
width="20.318001"
height="20.318001"
id="rect3204" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-139.785"
y="61.748001"
clip-rule="evenodd"
width="20.318001"
height="20.319"
id="rect3206" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-165.187"
y="10.951"
clip-rule="evenodd"
width="20.32"
height="20.318001"
id="rect3208" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-165.187"
y="36.349998"
clip-rule="evenodd"
width="20.32"
height="20.318001"
id="rect3210" />
<rect
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
x="-165.187"
y="61.748001"
clip-rule="evenodd"
width="20.32"
height="20.319"
id="rect3212" />
</g>
<path
style="fill:#464646;fill-opacity:1;fill-rule:evenodd"
inkscape:connector-curvature="0"
clip-rule="evenodd"
d="m -175.144,0.991 v 90.855 h 91.032 V 0.991 h -91.032 z m 84.395,84.217 h -77.757 V 7.63 h 77.757 v 77.578 z"
id="path3214" />
</g><g
style="opacity:0.4;fill:#464646;fill-opacity:1"
transform="matrix(0.27657836,0,0,0.27657836,373.31286,14.597384)"
id="g3288">
<g
style="fill:#464646;fill-opacity:1"
id="g3290">
<rect
id="rect3292"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="10.951"
x="-114.388"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3294"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="36.349998"
x="-114.388"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3296"
height="20.319"
width="20.32"
clip-rule="evenodd"
y="61.748001"
x="-114.388"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3298"
height="20.318001"
width="20.318001"
clip-rule="evenodd"
y="10.951"
x="-139.785"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3300"
height="20.318001"
width="20.318001"
clip-rule="evenodd"
y="36.349998"
x="-139.785"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3302"
height="20.319"
width="20.318001"
clip-rule="evenodd"
y="61.748001"
x="-139.785"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3304"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="10.951"
x="-165.187"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3306"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="36.349998"
x="-165.187"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3308"
height="20.319"
width="20.32"
clip-rule="evenodd"
y="61.748001"
x="-165.187"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
</g>
<path
id="path3310"
d="m -175.144,0.991 v 90.855 h 91.032 V 0.991 h -91.032 z m 84.395,84.217 h -77.757 V 7.63 h 77.757 v 77.578 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1;fill-rule:evenodd" />
</g><g
id="g3465"
transform="matrix(0.25404736,0,0,0.25404736,140.09745,14.744984)"
style="fill:#464646;fill-opacity:1"><g
id="g3451"
style="fill:#464646;fill-opacity:1">
<path
id="path3453"
d="m 28.793,71.207 c -5.062,-5.065 -7.6,-11.705 -7.62,-18.356 l -6.515,-0.002 c 0.021,8.32 3.193,16.635 9.526,22.967 6.335,6.331 14.647,9.504 22.967,9.526 L 47.15,78.827 C 40.5,78.806 33.856,76.268 28.793,71.207 z"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1" />
<path
id="path3455"
d="M 18.423,81.575 C 10.501,73.654 6.535,63.251 6.514,52.85 L 0,52.846 c 0.02,12.074 4.623,24.145 13.815,33.34 9.193,9.188 21.261,13.796 33.337,13.812 V 93.483 C 36.745,93.467 26.346,89.495 18.423,81.575 z"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1" />
</g><path
id="path3457"
d="M 76.157,43.675 73.294,43.334 C 77.075,38.692 76.803,31.847 72.475,27.523 68.154,23.196 61.309,22.924 56.664,26.705 L 56.325,23.843 32.482,0 11.531,20.949 l 23.842,23.843 2.864,0.341 c -3.779,4.643 -3.506,11.485 0.819,15.811 4.325,4.325 11.167,4.596 15.81,0.819 L 55.205,64.625 79.05,88.466 100,67.519 76.157,43.675 z M 38.384,38.586 20.749,20.949 32.482,9.218 l 17.637,17.636 0.669,5.64 -6.762,6.761 -5.642,-0.669 z M 79.05,79.249 61.412,61.613 l -0.668,-5.64 6.761,-6.761 5.642,0.668 17.633,17.639 -11.73,11.73 z"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1" /></g><g
id="g4314"
transform="matrix(0.24999998,0,0,0.24999998,200.32251,15.25725)"
style="fill:#464646;fill-opacity:1"><path
id="path4302"
d="M 49.541,0 C 22.198,-0.019 0.027,22.376 0,49.978 0.027,77.621 22.198,100.01 49.541,100 76.894,100.011 99.064,77.621 99.085,49.979 99.064,22.376 76.894,-0.019 49.541,0 z m 0,88.483 C 28.503,88.483 11.445,71.252 11.462,49.971 11.441,28.742 28.503,11.514 49.541,11.52 70.589,11.514 87.65,28.742 87.673,49.978 87.65,71.251 70.589,88.483 49.541,88.483 z"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1" /><path
id="path4304"
d="m 45.37,64.349 9.073,-0.041 v -7.015 c -0.023,-1.901 0.863,-3.704 3.242,-5.261 2.38,-1.563 9.016,-4.725 9.025,-13.061 C 66.7,30.67 59.73,24.955 53.868,23.72 48.014,22.529 41.665,23.324 37.169,28.281 c -4.042,4.396 -4.892,7.892 -4.905,15.603 h 9.07 v -1.799 c -0.014,-4.103 0.458,-8.438 6.312,-9.64 3.222,-0.631 6.226,0.375 8.018,2.103 2.024,2.007 2.042,6.496 -1.222,8.722 l -5.09,3.465 c -2.959,1.919 -3.977,4.038 -3.983,7.142 v 10.472 l 10e-4,0 z"
inkscape:connector-curvature="0"
style="fill:#464646;fill-opacity:1" /><polygon
id="polygon4306"
points="45.325,67.903 54.484,67.903 54.484,77.194 45.325,77.194 "
style="fill:#464646;fill-opacity:1" /></g><g
transform="matrix(0.84510875,0,0,0.84510875,246.47145,2.7863074)"
id="g4319"
style="opacity:0.4"><path
style="fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
d="m 19.02918,27.70898 0,-1.42397 1.52567,0 1.52569,0 0,-4.78047 0,-4.78046 -1.6274,0 -1.62738,0 0,1.52568 0,1.52568 -1.52567,0 -1.52569,0 0,-3.05136 0,-3.05135 7.83182,0 7.83182,0 0,3.05135 0,3.05136 -1.52569,0 -1.52567,0 0,-1.52568 0,-1.52568 -1.62739,0 -1.62739,0 0,4.78046 0,4.78047 1.52568,0 1.52567,0 0,1.42397 0,1.42396 -4.57703,0 -4.57704,0 0,-1.42396 z"
id="path4321"
inkscape:connector-curvature="0" /><path
style="fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
d="m 19.64356,39.48975 0,-5.51027 1.35498,0 1.35497,0 0,4.15529 0,4.15527 12.64649,0 12.6465,0 0,-9.8462 0,-9.84619 -5.60059,0 -5.60059,0 0,-1.26465 0,-1.26465 6.95558,0 6.95556,0 0,12.46582 0,12.46583 L 35,45 l -15.35644,0 0,-5.51025 z"
id="path4323"
inkscape:connector-curvature="0" /></g><g
style="opacity:0.4;fill:#464646;fill-opacity:1"
transform="matrix(0.24999998,0,0,0.24999998,445.38927,15.676751)"
id="g4325"><path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 49.541,0 C 22.198,-0.019 0.027,22.376 0,49.978 0.027,77.621 22.198,100.01 49.541,100 76.894,100.011 99.064,77.621 99.085,49.979 99.064,22.376 76.894,-0.019 49.541,0 z m 0,88.483 C 28.503,88.483 11.445,71.252 11.462,49.971 11.441,28.742 28.503,11.514 49.541,11.52 70.589,11.514 87.65,28.742 87.673,49.978 87.65,71.251 70.589,88.483 49.541,88.483 z"
id="path4327" /><path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 45.37,64.349 9.073,-0.041 v -7.015 c -0.023,-1.901 0.863,-3.704 3.242,-5.261 2.38,-1.563 9.016,-4.725 9.025,-13.061 C 66.7,30.67 59.73,24.955 53.868,23.72 48.014,22.529 41.665,23.324 37.169,28.281 c -4.042,4.396 -4.892,7.892 -4.905,15.603 h 9.07 v -1.799 c -0.014,-4.103 0.458,-8.438 6.312,-9.64 3.222,-0.631 6.226,0.375 8.018,2.103 2.024,2.007 2.042,6.496 -1.222,8.722 l -5.09,3.465 c -2.959,1.919 -3.977,4.038 -3.983,7.142 v 10.472 l 10e-4,0 z"
id="path4329" /><polygon
style="fill:#464646;fill-opacity:1"
points="45.325,67.903 54.484,67.903 54.484,77.194 45.325,77.194 "
id="polygon4331" /></g><g
style="opacity:0.4;fill:#464646;fill-opacity:1"
transform="matrix(0.25404736,0,0,0.25404736,384.37987,15.991112)"
id="g4333"><g
style="fill:#464646;fill-opacity:1"
id="g4335">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 28.793,71.207 c -5.062,-5.065 -7.6,-11.705 -7.62,-18.356 l -6.515,-0.002 c 0.021,8.32 3.193,16.635 9.526,22.967 6.335,6.331 14.647,9.504 22.967,9.526 L 47.15,78.827 C 40.5,78.806 33.856,76.268 28.793,71.207 z"
id="path4337" />
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 18.423,81.575 C 10.501,73.654 6.535,63.251 6.514,52.85 L 0,52.846 c 0.02,12.074 4.623,24.145 13.815,33.34 9.193,9.188 21.261,13.796 33.337,13.812 V 93.483 C 36.745,93.467 26.346,89.495 18.423,81.575 z"
id="path4339" />
</g><path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 76.157,43.675 73.294,43.334 C 77.075,38.692 76.803,31.847 72.475,27.523 68.154,23.196 61.309,22.924 56.664,26.705 L 56.325,23.843 32.482,0 11.531,20.949 l 23.842,23.843 2.864,0.341 c -3.779,4.643 -3.506,11.485 0.819,15.811 4.325,4.325 11.167,4.596 15.81,0.819 L 55.205,64.625 79.05,88.466 100,67.519 76.157,43.675 z M 38.384,38.586 20.749,20.949 32.482,9.218 l 17.637,17.636 0.669,5.64 -6.762,6.761 -5.642,-0.669 z M 79.05,79.249 61.412,61.613 l -0.668,-5.64 6.761,-6.761 5.642,0.668 17.633,17.639 -11.73,11.73 z"
id="path4341" /></g><rect
style="opacity:0.8;fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3039"
width="250.00003"
height="60"
x="490"
y="-1.7162162e-06" /><g
transform="matrix(0.84510875,0,0,0.84510875,492.46665,4.1596899)"
id="g3041"
style="fill:#ffffff"><path
style="fill:#ffffff;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
d="m 19.02918,27.70898 0,-1.42397 1.52567,0 1.52569,0 0,-4.78047 0,-4.78046 -1.6274,0 -1.62738,0 0,1.52568 0,1.52568 -1.52567,0 -1.52569,0 0,-3.05136 0,-3.05135 7.83182,0 7.83182,0 0,3.05135 0,3.05136 -1.52569,0 -1.52567,0 0,-1.52568 0,-1.52568 -1.62739,0 -1.62739,0 0,4.78046 0,4.78047 1.52568,0 1.52567,0 0,1.42397 0,1.42396 -4.57703,0 -4.57704,0 0,-1.42396 z"
id="path3043"
inkscape:connector-curvature="0" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
d="m 19.64356,39.48975 0,-5.51027 1.35498,0 1.35497,0 0,4.15529 0,4.15527 12.64649,0 12.6465,0 0,-9.8462 0,-9.84619 -5.60059,0 -5.60059,0 0,-1.26465 0,-1.26465 6.95558,0 6.95556,0 0,12.46582 0,12.46583 L 35,45 l -15.35644,0 0,-5.51025 z"
id="path3045"
inkscape:connector-curvature="0" /></g><g
style="fill:#ffffff;fill-opacity:1"
transform="matrix(0.27873303,0,0,0.27873303,618.96272,15.125216)"
id="g3047">
<g
style="fill:#ffffff;fill-opacity:1"
id="g3049">
<rect
id="rect3051"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="10.951"
x="-114.388"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3053"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="36.349998"
x="-114.388"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3055"
height="20.319"
width="20.32"
clip-rule="evenodd"
y="61.748001"
x="-114.388"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3057"
height="20.318001"
width="20.318001"
clip-rule="evenodd"
y="10.951"
x="-139.785"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3059"
height="20.318001"
width="20.318001"
clip-rule="evenodd"
y="36.349998"
x="-139.785"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3061"
height="20.319"
width="20.318001"
clip-rule="evenodd"
y="61.748001"
x="-139.785"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3063"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="10.951"
x="-165.187"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3065"
height="20.318001"
width="20.32"
clip-rule="evenodd"
y="36.349998"
x="-165.187"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
<rect
id="rect3067"
height="20.319"
width="20.32"
clip-rule="evenodd"
y="61.748001"
x="-165.187"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
</g>
<path
id="path3069"
d="m -175.144,0.991 v 90.855 h 91.032 V 0.991 h -91.032 z m 84.395,84.217 h -77.757 V 7.63 h 77.757 v 77.578 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
</g><g
style="fill:#ffffff;fill-opacity:1"
transform="matrix(0.25404736,0,0,0.25404736,630.09747,15.002146)"
id="g3071"><g
style="fill:#ffffff;fill-opacity:1"
id="g3073">
<path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 28.793,71.207 c -5.062,-5.065 -7.6,-11.705 -7.62,-18.356 l -6.515,-0.002 c 0.021,8.32 3.193,16.635 9.526,22.967 6.335,6.331 14.647,9.504 22.967,9.526 L 47.15,78.827 C 40.5,78.806 33.856,76.268 28.793,71.207 z"
id="path3075" />
<path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 18.423,81.575 C 10.501,73.654 6.535,63.251 6.514,52.85 L 0,52.846 c 0.02,12.074 4.623,24.145 13.815,33.34 9.193,9.188 21.261,13.796 33.337,13.812 V 93.483 C 36.745,93.467 26.346,89.495 18.423,81.575 z"
id="path3077" />
</g><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 76.157,43.675 73.294,43.334 C 77.075,38.692 76.803,31.847 72.475,27.523 68.154,23.196 61.309,22.924 56.664,26.705 L 56.325,23.843 32.482,0 11.531,20.949 l 23.842,23.843 2.864,0.341 c -3.779,4.643 -3.506,11.485 0.819,15.811 4.325,4.325 11.167,4.596 15.81,0.819 L 55.205,64.625 79.05,88.466 100,67.519 76.157,43.675 z M 38.384,38.586 20.749,20.949 32.482,9.218 l 17.637,17.636 0.669,5.64 -6.762,6.761 -5.642,-0.669 z M 79.05,79.249 61.412,61.613 l -0.668,-5.64 6.761,-6.761 5.642,0.668 17.633,17.639 -11.73,11.73 z"
id="path3079" /></g><g
style="fill:#ffffff;fill-opacity:1"
transform="matrix(0.24999998,0,0,0.24999998,690.32253,15.514412)"
id="g3081"><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
d="M 49.541,0 C 22.198,-0.019 0.027,22.376 0,49.978 0.027,77.621 22.198,100.01 49.541,100 76.894,100.011 99.064,77.621 99.085,49.979 99.064,22.376 76.894,-0.019 49.541,0 z m 0,88.483 C 28.503,88.483 11.445,71.252 11.462,49.971 11.441,28.742 28.503,11.514 49.541,11.52 70.589,11.514 87.65,28.742 87.673,49.978 87.65,71.251 70.589,88.483 49.541,88.483 z"
id="path3083" /><path
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 45.37,64.349 9.073,-0.041 v -7.015 c -0.023,-1.901 0.863,-3.704 3.242,-5.261 2.38,-1.563 9.016,-4.725 9.025,-13.061 C 66.7,30.67 59.73,24.955 53.868,23.72 48.014,22.529 41.665,23.324 37.169,28.281 c -4.042,4.396 -4.892,7.892 -4.905,15.603 h 9.07 v -1.799 c -0.014,-4.103 0.458,-8.438 6.312,-9.64 3.222,-0.631 6.226,0.375 8.018,2.103 2.024,2.007 2.042,6.496 -1.222,8.722 l -5.09,3.465 c -2.959,1.919 -3.977,4.038 -3.983,7.142 v 10.472 l 10e-4,0 z"
id="path3085" /><polygon
style="fill:#ffffff;fill-opacity:1"
points="45.325,77.194 45.325,67.903 54.484,67.903 54.484,77.194 "
id="polygon3087" /></g></svg>

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="260" viewBox="0 0 26 260" >
<g id="sprite" fill="#000000">
<path
id="zoomIn"
d="m 12.5,8.99999 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,2.50002 -2.5,0 c -0.277,0 -0.5,0.223 -0.5,0.5 l 0,1 c 0,0.277 0.223,0.5 0.5,0.5 l 2.5,0 0,2.5 c 0,0.277 0.223,0.5 0.5,0.5 l 1,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-2.5 2.5,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 l -2.5,0 0,-2.50002 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z" />
<path
id="zoomOut"
d="M 9.5,38 C 9.223,38 9,38.223 9,38.5 l 0,1 C 9,39.777 9.223,40 9.5,40 l 7,0 c 0.277,0 0.5,-0.223 0.5,-0.5 l 0,-1 C 17,38.223 16.777,38 16.5,38 Z" />
<path
id="close"
d="m 10,61 -1,1 3,3 -3,3 1,1 3,-3 3,3 1,-1 -3,-3 3,-3 -1,-1 -3,3 z" />
<path
id="info"
d="m 13,83.99999 c -3.866,0 -7,3.134 -7,7.00002 0,3.866 3.134,7 7,7 3.8659,0 7,-3.134 7,-7 0,-3.86602 -3.1341,-7.00002 -7,-7.00002 z m -0.5,3 1,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,1 c 0,0.27702 -0.223,0.50002 -0.5,0.50002 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.50002 l 0,-1 c 0,-0.277 0.223,-0.5 0.5,-0.5 z m 0,3.00002 1,0 c 0.277,0 0.5,0.223 0.5,0.5 l 0,4 c 0,0.277 -0.223,0.5 -0.5,0.5 l -1,0 c -0.277,0 -0.5,-0.223 -0.5,-0.5 l 0,-4 c 0,-0.277 0.223,-0.5 0.5,-0.5 z" />
<path
id="layers"
d="m 13,110.96875 c -0.1012,-5e-4 -0.19265,0.0145 -0.28125,0.0625 L 6,113 l 0,1 6.71875,1.9375 C 12.80675,115.9855 12.8995,116 13,116 c 0.1001,0 0.19325,-0.0145 0.28125,-0.0625 L 20,114 l 0,-1 -6.71875,-1.96875 c -0.088,-0.048 -0.18155,-0.0625 -0.28125,-0.0625 z M 6,118 l 6.71875,1.9375 C 12.80675,119.9855 12.8995,120 13,120 c 0.1001,0 0.19325,-0.0145 0.28125,-0.0625 L 20,118 l 0,-2 -6.71875,1.9375 C 13.19325,117.9855 13.1001,118 13,118 c -0.1005,0 -0.19325,-0.0145 -0.28125,-0.0625 L 6,116 Z m 0,4 6.71875,1.9375 C 12.80675,123.9855 12.8995,124 13,124 c 0.1001,0 0.19325,-0.0145 0.28125,-0.0625 L 20,122 l 0,-2 -6.71875,1.9375 C 13.19325,121.9855 13.1001,122 13,122 c -0.1005,0 -0.19325,-0.0145 -0.28125,-0.0625 L 6,120 Z" />
<path
id="share"
d="m 14.3438,136.99999 2.125,2.125 -4.25,4.25002 1.4062,1.4062 4.25,-4.25002 2.125,2.12502 0,-5.65622 z m -6.3438,1 -1,1 0,9.00002 1,1 10,0 1,-1 0,-4 -1,-1 -1,1 0,3 -8,0 0,-7.00002 4,0 1,-1 -1,-1 z" />
<path
id="search"
d="m 11.5,162.99999 c -2.4853,0 -4.5,2.0147 -4.5,4.50002 0,2.4853 2.0147,4.5 4.5,4.5 0.854,0 1.6635,-0.2431 2.3437,-0.6563 l 3.6563,3.6563 1.5,-1.5 -3.6563,-3.6563 c 0.4132,-0.6802 0.6563,-1.4897 0.6563,-2.3437 0,-2.48532 -2.0147,-4.50002 -4.5,-4.50002 z m 0,1.5 c 1.6568,0 3,1.3431 3,3.00002 0,1.6568 -1.3432,3 -3,3 -1.6569,0 -3,-1.3432 -3,-3 0,-1.65692 1.3431,-3.00002 3,-3.00002 z" />
<path
id="facebook"
d="m 7,186.99998 -1,1 0,12 1,1 6.81812,1e-5 0,-5.00001 -1.8245,0 0,-2 1.8245,0 0,-2.09264 c 0,-1.80824 1.10439,-2.79288 2.7175,-2.79288 0.77272,0 1.09654,0.0575 1.29005,0.0832 l 0,1.80232 -0.77851,5.2e-4 C 16.1699,191.0005 16,191.41736 16,192.0291 l 0,1.97088 2.09228,0 -0.2724,2 -1.81988,0 0,5.00001 3,-1e-5 1,-1 0,-12 -1,-1 z" />
<path
id="twitter"
d="m 19.99957,216.12602 c -0.51509,0.22492 -1.06868,0.37693 -1.64967,0.44524 0.59299,-0.34993 1.04838,-0.90408 1.26288,-1.56444 -0.55499,0.32413 -1.16968,0.55945 -1.82397,0.68616 C 17.26502,215.14343 16.51853,214.8 15.69245,214.8 c -1.58627,0 -2.87235,1.26611 -2.87235,2.82775 0,0.22164 0.025,0.43756 0.075,0.64448 -2.38705,-0.11791 -4.50341,-1.24373 -5.91999,-2.95458 -0.24719,0.41763 -0.38889,0.90338 -0.38889,1.42162 0,0.98111 0.50699,1.84658 1.27768,2.35373 -0.4708,-0.015 -0.91369,-0.14191 -1.30088,-0.35363 -3e-4,0.012 -3e-4,0.024 -3e-4,0.035 0,1.37002 0.99008,2.51291 2.30396,2.77274 -0.241,0.065 -0.49469,0.099 -0.75669,0.099 -0.1851,0 -0.36499,-0.018 -0.54029,-0.051 0.36539,1.1234 1.42607,1.94097 2.68295,1.96377 -0.98298,0.75847 -2.22136,1.21061 -3.56713,1.21061 -0.2318,0 -0.46039,-0.013 -0.68509,-0.04 C 7.27151,225.53176 8.78128,226 10.40325,226 c 5.2831,0 8.17205,-4.30888 8.17205,-8.0457 0,-0.12261 0,-0.24462 -0.01,-0.36585 0.56129,-0.39874 1.04818,-0.89668 1.43327,-1.46383" />
<path
id="pinterest"
d="M 13.50024,238.9996 C 9.35822,238.9996 6,242.35751 6,246.49969 c 0,3.07113 1.84674,5.70949 4.48939,6.86948 -0.0211,-0.5237 -0.004,-1.15239 0.13052,-1.7222 0.14421,-0.60892 0.96507,-4.08682 0.96507,-4.08682 0,0 -0.23961,-0.47889 -0.23961,-1.18669 0,-1.11141 0.64421,-1.94155 1.44648,-1.94155 0.68215,0 1.01169,0.51241 1.01169,1.12601 0,0.68581 -0.43735,1.71156 -0.66231,2.66165 -0.18789,0.79559 0.39892,1.44451 1.1838,1.44451 1.42105,0 2.37808,-1.82512 2.37808,-3.98753 0,-1.64383 -1.10715,-2.87415 -3.12081,-2.87415 -2.27506,0 -3.69243,1.69664 -3.69243,3.59178 0,0.6534 0.19268,1.11426 0.49445,1.47102 0.13874,0.1639 0.15803,0.22983 0.10778,0.418 -0.0359,0.13804 -0.11858,0.47026 -0.15277,0.60191 -0.05,0.18998 -0.20384,0.25786 -0.37553,0.18774 -1.04789,-0.4278 -1.53596,-1.5754 -1.53596,-2.86536 0,-2.13055 1.79685,-4.68521 5.36034,-4.68521 2.86349,0 4.74808,2.0721 4.74808,4.29635 0,2.94222 -1.63569,5.14028 -4.04684,5.14028 -0.8097,0 -1.57134,-0.43774 -1.83228,-0.9349 0,0 -0.43543,1.72808 -0.52766,2.06171 -0.15897,0.57829 -0.47026,1.1562 -0.75486,1.60673 0.67461,0.19909 1.3871,0.30755 2.12562,0.30755 4.1417,0 7.49977,-3.35795 7.49977,-7.50031 0,-4.14218 -3.35807,-7.50009 -7.49977,-7.50009" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="1080"
height="60"
viewBox="0 0 1080 59.999999"
enable-background="new 0 0 100 100"
xml:space="preserve"
inkscape:version="0.48.2 r9819"
sodipodi:docname="spritesheet_new.svg"
inkscape:export-filename="/Users/aaronr/github/bboxfinder.com/css/maps/images/spritesheet_new.png"
inkscape:export-xdpi="45"
inkscape:export-ydpi="45"><metadata
id="metadata51"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs49"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 50 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="100 : 50 : 1"
inkscape:persp3d-origin="50 : 33.333333 : 1"
id="perspective5373" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1226"
inkscape:window-height="844"
id="namedview47"
showgrid="true"
inkscape:zoom="0.83039936"
inkscape:cx="502.47411"
inkscape:cy="27.069218"
inkscape:window-x="197"
inkscape:window-y="15"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
borderlayer="false"><inkscape:grid
type="xygrid"
id="grid6480"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" /></sodipodi:namedview>
<path
inkscape:connector-curvature="0"
id="path6172"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6178"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6184"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6186"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6270"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6288"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6682"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path
inkscape:connector-curvature="0"
id="path6684"
d=""
style="fill:#bbbbbb;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><g
style="fill:#464646;fill-opacity:1"
id="g3558"
transform="translate(-1.07,-5e-7)">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 18,36 v 6 h 6 v -6 h -6 z m 4,4 h -2 v -2 h 2 v 2 z"
id="path3560" />
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 36,18 v 6 h 6 v -6 h -6 z m 4,4 h -2 v -2 h 2 v 2 z"
id="path3562" />
<polygon
style="fill:#464646;fill-opacity:1"
points="20.857,36.854 36.857,20.857 39.142,23.142 23.142,39.145 "
id="polygon3564" />
</g><polygon
style="fill:#464646;fill-opacity:1"
points="100,24.565 97.904,39.396 83.07,42 76,28.773 86.463,18 "
id="polygon3566"
transform="translate(-1.07,-5e-7)" /><rect
style="fill:#464646;fill-opacity:1"
x="138.92999"
y="20"
width="20"
height="20"
id="rect3568" /><path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 219.93,30 c 0,6.078 -4.926,11 -11,11 -6.074,0 -11,-4.922 -11,-11 0,-6.074 4.926,-11 11,-11 6.074,0 11,4.926 11,11 z"
id="path3570" /><path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 268.93,19 c -4.971,0 -9,4.028999 -9,9 0,4.971 5.001,12 9,14 4.001,-2 9,-9.029 9,-14 0,-4.971001 -4.029,-9 -9,-9 z m 0,12.5 c -2.484,0 -4.5,-2.014 -4.5,-4.5 0,-2.484001 2.016,-4.5 4.5,-4.5 2.485,0 4.5,2.015999 4.5,4.5 0,2.486 -2.015,4.5 -4.5,4.5 z"
id="path3572" /><g
style="fill:#464646;fill-opacity:1"
id="g3574"
transform="translate(-1.07,-5e-7)">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 337,30.156 v 0.407 5.604 c 0,1.658 -1.344,3 -3,3 h -10 c -1.655,0 -3,-1.342 -3,-3 v -10 c 0,-1.657 1.345,-3 3,-3 h 6.345 l 3.19,-3.17 H 324 c -3.313,0 -6,2.687 -6,6 v 10 c 0,3.313 2.687,6 6,6 h 10 c 3.314,0 6,-2.687 6,-6 v -8.809 l -3,2.968"
id="path3576" />
<polygon
style="fill:#464646;fill-opacity:1"
points="335.891,21.809 338.719,24.637 329.828,33.529 327,33.529 327,30.699 "
id="polygon3578" />
<rect
style="fill:#464646;fill-opacity:1"
x="338.69699"
y="17.826"
transform="matrix(-0.707,-0.7072,0.7072,-0.707,567.5655,274.7729)"
width="4"
height="4"
id="rect3580" />
</g><g
style="fill:#464646;fill-opacity:1"
id="g3582"
transform="translate(-1.07,-5e-7)">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 381,42 h 18 V 24 h -18 v 18 z m 14,-16 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z"
id="path3584" />
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 395,20 v -2 -2 h -10 v 2 2 h -6 v 2 h 22 v -2 h -6 z m -2,0 h -6 v -2 h 6 v 2 z"
id="path3586" />
</g><g
style="opacity:0.4;fill:#464646;fill-opacity:1"
id="g3588"
transform="translate(-1.07,-5e-7)">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 458,30.156 v 0.407 5.604 c 0,1.658 -1.344,3 -3,3 h -10 c -1.655,0 -3,-1.342 -3,-3 v -10 c 0,-1.657 1.345,-3 3,-3 h 6.345 l 3.19,-3.17 H 445 c -3.313,0 -6,2.687 -6,6 v 10 c 0,3.313 2.687,6 6,6 h 10 c 3.314,0 6,-2.687 6,-6 v -8.809 l -3,2.968"
id="path3590" />
<polygon
style="fill:#464646;fill-opacity:1"
points="456.891,21.809 459.719,24.637 450.828,33.529 448,33.529 448,30.699 "
id="polygon3592" />
<rect
style="fill:#464646;fill-opacity:1"
x="459.69699"
y="17.826"
transform="matrix(-0.707,-0.7072,0.7072,-0.707,774.118,360.3402)"
width="4"
height="4"
id="rect3594" />
</g><g
style="opacity:0.4;fill:#464646;fill-opacity:1"
id="g3596"
transform="translate(-1.07,-5e-7)">
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 501,42 h 18 V 24 h -18 v 18 z m 14,-16 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z"
id="path3598" />
<path
style="fill:#464646;fill-opacity:1"
inkscape:connector-curvature="0"
d="m 515,20 v -2 -2 h -10 v 2 2 h -6 v 2 h 22 v -2 h -6 z m -2,0 h -6 v -2 h 6 v 2 z"
id="path3600" />
</g><rect
style="opacity:0.4;fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
id="rect3684"
width="540"
height="60"
x="542"
y="1.9999995" /><path
style="opacity:0.8;fill:#464646;fill-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
d="m 542.50845,29.920029 0,-29.0322577 268.74579,0 268.74576,0 0,29.0322577 0,29.032258 -268.74576,0 -268.74579,0 0,-29.032258 z"
id="path3686"
inkscape:connector-curvature="0" /><g
transform="translate(540,-1.0000005)"
id="g3654"
style="fill:#ffffff;fill-opacity:1">
<path
id="path3656"
d="m 18,36 v 6 h 6 v -6 h -6 z m 4,4 h -2 v -2 h 2 v 2 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" />
<path
id="path3658"
d="m 36,18 v 6 h 6 v -6 h -6 z m 4,4 h -2 v -2 h 2 v 2 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" />
<polygon
id="polygon3660"
points="36.857,20.857 39.142,23.142 23.142,39.145 20.857,36.854 "
style="fill:#ffffff;fill-opacity:1" />
</g><polygon
transform="translate(540,-1.0000005)"
id="polygon3662"
points="100,24.565 97.904,39.396 83.07,42 76,28.773 86.463,18 "
style="fill:#ffffff;fill-opacity:1" /><rect
id="rect3664"
height="20"
width="20"
y="19"
x="680"
style="fill:#ffffff;fill-opacity:1" /><path
id="path3666"
d="m 761,29 c 0,6.078001 -4.926,11 -11,11 -6.074,0 -11,-4.921999 -11,-11 0,-6.073999 4.926,-10.999999 11,-10.999999 6.074,0 11,4.926 11,10.999999 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" /><path
id="path3668"
d="m 810,18.000001 c -4.971,0 -9,4.028999 -9,8.999999 0,4.971001 5.001,12 9,14 4.001,-2 9,-9.028999 9,-14 0,-4.971 -4.029,-8.999999 -9,-8.999999 z M 810,30.5 c -2.484,0 -4.5,-2.013999 -4.5,-4.5 0,-2.483999 2.016,-4.5 4.5,-4.5 2.485,0 4.5,2.016001 4.5,4.5 0,2.486001 -2.015,4.5 -4.5,4.5 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" /><g
transform="translate(540,-1.0000005)"
id="g3670"
style="fill:#ffffff;fill-opacity:1">
<path
id="path3672"
d="m 337,30.156 v 0.407 5.604 c 0,1.658 -1.344,3 -3,3 h -10 c -1.655,0 -3,-1.342 -3,-3 v -10 c 0,-1.657 1.345,-3 3,-3 h 6.345 l 3.19,-3.17 H 324 c -3.313,0 -6,2.687 -6,6 v 10 c 0,3.313 2.687,6 6,6 h 10 c 3.314,0 6,-2.687 6,-6 v -8.809 l -3,2.968"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" />
<polygon
id="polygon3674"
points="335.891,21.809 338.719,24.637 329.828,33.529 327,33.529 327,30.699 "
style="fill:#ffffff;fill-opacity:1" />
<rect
id="rect3676"
height="4"
width="4"
transform="matrix(-0.707,-0.7072,0.7072,-0.707,567.5655,274.7729)"
y="17.826"
x="338.69699"
style="fill:#ffffff;fill-opacity:1" />
</g><g
transform="translate(540,-1.0000005)"
id="g3678"
style="fill:#ffffff;fill-opacity:1">
<path
id="path3680"
d="m 381,42 h 18 V 24 h -18 v 18 z m 14,-16 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z m -4,0 h 2 v 14 h -2 V 26 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" />
<path
id="path3682"
d="m 395,20 v -2 -2 h -10 v 2 2 h -6 v 2 h 22 v -2 h -6 z m -2,0 h -6 v -2 h 6 v 2 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1" />
</g></svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,478 @@
/* required styles */
.leaflet-map-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img {
max-width: none !important;
}
/* stupid Android 2 doesn't understand "max-width: none" properly */
.leaflet-container img.leaflet-image-layer {
max-width: 15000px !important;
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-tile-pane { z-index: 2; }
.leaflet-objects-pane { z-index: 3; }
.leaflet-overlay-pane { z-index: 4; }
.leaflet-shadow-pane { z-index: 5; }
.leaflet-marker-pane { z-index: 6; }
.leaflet-popup-pane { z-index: 7; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 7;
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile,
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-clickable {
cursor: pointer;
}
.leaflet-container {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-container,
.leaflet-dragging .leaflet-clickable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-control-zoom-out {
font-size: 20px;
}
.leaflet-touch .leaflet-control-zoom-in {
font-size: 22px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 24px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: content-box;
box-sizing: content-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}

248
gui-src/css/maps/leaflet.draw.css vendored Normal file
View File

@@ -0,0 +1,248 @@
/* ================================================================== */
/* Toolbars
/* ================================================================== */
.leaflet-draw-section {
position: relative;
}
.leaflet-draw-toolbar {
margin-top: 12px;
}
.leaflet-draw-toolbar-top {
margin-top: 0;
}
.leaflet-draw-toolbar-notop a:first-child {
border-top-right-radius: 0;
}
.leaflet-draw-toolbar-nobottom a:last-child {
border-bottom-right-radius: 0;
}
.leaflet-draw-toolbar a {
background-image: url('images/spritesheet.png');
background-repeat: no-repeat;
}
.leaflet-retina .leaflet-draw-toolbar a {
background-image: url('images/spritesheet-2x.png');
background-size: 210px 30px;
}
.leaflet-draw a {
display: block;
text-align: center;
text-decoration: none;
}
/* ================================================================== */
/* Toolbar actions menu
/* ================================================================== */
.leaflet-draw-actions {
display: none;
list-style: none;
margin: 0;
padding: 0;
position: absolute;
left: 26px; /* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
top: 0;
}
.leaflet-right .leaflet-draw-actions {
right:26px;
left:auto;
}
.leaflet-draw-actions li {
display: inline-block;
}
/* APR */
.leaflet-draw-actions li a {
color: #ffffff;
}
.leaflet-draw-actions li:first-child a {
border-left: none;
}
.leaflet-draw-actions li:last-child a {
-webkit-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.leaflet-right .leaflet-draw-actions li:last-child a {
-webkit-border-radius: 0;
border-radius: 0;
}
.leaflet-right .leaflet-draw-actions li:first-child a {
-webkit-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.leaflet-draw-actions a {
background-color: #919187;
border-left: 1px solid #AAA;
color: #FFF;
font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
line-height: 28px;
text-decoration: none;
padding-left: 10px;
padding-right: 10px;
height: 28px;
}
.leaflet-draw-actions-bottom {
margin-top: 0;
white-space: nowrap;
}
.leaflet-draw-actions-top {
margin-top: 1px;
white-space: nowrap;
}
.leaflet-draw-actions-top a,
.leaflet-draw-actions-bottom a {
height: 27px;
line-height: 27px;
}
.leaflet-draw-actions a:hover {
background-color: #A0A098;
}
.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
height: 26px;
line-height: 26px;
}
/* ================================================================== */
/* Draw toolbar
/* ================================================================== */
.leaflet-draw-toolbar .leaflet-draw-draw-polyline {
background-position: -2px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-polygon {
background-position: -31px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
background-position: -62px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-circle {
background-position: -92px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-draw-marker {
background-position: -122px -2px;
}
/* ================================================================== */
/* Edit toolbar
/* ================================================================== */
.leaflet-draw-toolbar .leaflet-draw-edit-edit {
background-position: -152px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-remove {
background-position: -182px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
background-position: -212px -2px;
}
.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
background-position: -242px -2px;
}
/* ================================================================== */
/* Drawing styles
/* ================================================================== */
.leaflet-mouse-marker {
background-color: #fff;
cursor: crosshair;
}
.leaflet-draw-tooltip {
background: rgb(54, 54, 54);
background: rgba(0, 0, 0, 0.5);
border: 1px solid transparent;
-webkit-border-radius: 4px;
border-radius: 4px;
color: #fff;
font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
margin-left: 20px;
margin-top: -21px;
padding: 4px 8px;
position: absolute;
white-space: nowrap;
z-index: 6;
}
.leaflet-draw-tooltip:before {
border-right: 6px solid black;
border-right-color: rgba(0, 0, 0, 0.5);
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
content: "";
position: absolute;
top: 7px;
left: -7px;
}
.leaflet-error-draw-tooltip {
background-color: #F2DEDE;
border: 1px solid #E6B6BD;
color: #B94A48;
}
.leaflet-error-draw-tooltip:before {
border-right-color: #E6B6BD;
}
.leaflet-draw-tooltip-single {
margin-top: -12px
}
.leaflet-draw-tooltip-subtext {
color: #f8d5e4;
}
.leaflet-draw-guide-dash {
font-size: 1%;
opacity: 0.6;
position: absolute;
width: 5px;
height: 5px;
}
/* ================================================================== */
/* Edit styles
/* ================================================================== */
.leaflet-edit-marker-selected {
background: rgba(254, 87, 161, 0.1);
border: 4px dashed rgba(254, 87, 161, 0.6);
-webkit-border-radius: 4px;
border-radius: 4px;
}
.leaflet-edit-move {
cursor: move;
}
.leaflet-edit-resize {
cursor: pointer;
}

View File

@@ -0,0 +1,49 @@
/* Conditional stylesheet for IE. */
.leaflet-draw-toolbar {
border: 3px solid #999;
}
.leaflet-draw-toolbar a {
background-color: #eee;
}
.leaflet-draw-toolbar a:hover {
background-color: #fff;
}
.leaflet-draw-actions {
left: 32px;
margin-top: 3px;
}
.leaflet-draw-actions li {
display: inline;
zoom: 1;
}
.leaflet-edit-marker-selected {
border: 4px dashed #fe93c2;
}
.leaflet-draw-actions a {
background-color: #999;
}
.leaflet-draw-actions a:hover {
background-color: #a5a5a5;
}
.leaflet-draw-actions-top a {
margin-top: 1px;
}
.leaflet-draw-actions-bottom a {
height: 28px;
line-height: 28px;
}
.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
height: 27px;
line-height: 27px;
}

View File

@@ -0,0 +1,51 @@
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
.leaflet-control {
display: inline;
}
.leaflet-popup-tip {
width: 21px;
_width: 27px;
margin: 0 auto;
_margin-top: -3px;
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
}
.leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
border: 1px solid #999;
}
.leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-control-zoom,
.leaflet-control-layers {
border: 3px solid #999;
}
.leaflet-control-layers-toggle {
}
.leaflet-control-attribution,
.leaflet-control-layers,
.leaflet-control-scale-line {
background: white;
}
.leaflet-zoom-box {
filter: alpha(opacity=50);
}
.leaflet-control-attribution {
border-top: 1px solid #bbb;
border-left: 1px solid #bbb;
}

View File

@@ -0,0 +1,159 @@
.leaflet-sidebar {
position: absolute;
height: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
z-index: 20000;
}
.leaflet-sidebar.left {
left: -700px;
transition: left 0.5s, width 0.5s;
padding-right: 0;
}
.leaflet-sidebar.right {
right: -700px;
transition: right 0.5s, width 0.5s;
padding-left: 0;
}
.leaflet-sidebar.left.visible {
left: 0;
}
.leaflet-sidebar.right.visible {
right: 0;
}
.leaflet-left {
transition: left 0.5s;
}
.leaflet-right {
transition: right 0.5s;
}
.leaflet-sidebar > .leaflet-control {
height: 100%;
width: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 8px 24px;
font-size: 1.1em;
background: white;
box-shadow: 0 1px 7px rgba(0,0,0,0.65);
-webkit-border-radius: 4px;
border-radius: 4px;
}
@media(max-width:767px){
.leaflet-sidebar {
width: 100%;
padding: 0;
}
.leaflet-sidebar.left {
left: -100%;
}
.leaflet-sidebar.right {
right: -100%;
}
.leaflet-sidebar.left.visible {
left: 0;
}
.leaflet-sidebar.right.visible {
right: 0;
}
.leaflet-sidebar > .leaflet-control {
box-shadow: none;
-webkit-border-radius: 0;
border-radius: 0;
}
.leaflet-sidebar.left.visible ~ .leaflet-left {
left: 100%;
}
.leaflet-sidebar.right.visible ~ .leaflet-right {
right: 100%;
}
}
@media(min-width:768px) and (max-width:991px){
.leaflet-sidebar {
width: 505px;
}
.leaflet-sidebar.left.visible ~ .leaflet-left {
left: 505px;
}
.leaflet-sidebar.right.visible ~ .leaflet-right {
right: 505px;
}
}
@media(min-width:992px) and (max-width:1199px){
.leaflet-sidebar {
width: 590px;
}
.leaflet-sidebar.left.visible ~ .leaflet-left {
left: 590px;
}
.leaflet-sidebar.right.visible ~ .leaflet-right {
right: 590px;
}
}
@media(min-width:1200px){
.leaflet-sidebar {
width: 660px;
}
.leaflet-sidebar.left.visible ~ .leaflet-left {
left: 660px;
}
.leaflet-sidebar.right.visible ~ .leaflet-right {
right: 660px;
}
}
.leaflet-sidebar .close {
position: absolute;
right: 20px;
top: 20px;
width: 31px;
height: 31px;
color: #333;
font-size: 25pt;
line-height: 1em;
text-align: center;
background: white;
-webkit-border-radius: 16px;
border-radius: 16px;
cursor: pointer;
z-index: 8;
}

View File

@@ -0,0 +1,757 @@
/* general typography */
.leaflet-container {
background:#fff;
font:15px/25px 'Helvetica Neue', Arial, Helvetica, sans-serif;
color:#404040;
color:rgba(0,0,0,0.75);
outline:0;
overflow:hidden;
-ms-touch-action:none;
}
.leaflet-container *,
.leaflet-container *:after,
.leaflet-container *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
.leaflet-container h1,
.leaflet-container h2,
.leaflet-container h3,
.leaflet-container h4,
.leaflet-container h5,
.leaflet-container h6,
.leaflet-container p {
font-size:15px;
line-height:25px;
margin:0 0 10px;
}
.mapbox-small,
.leaflet-control-attribution,
.leaflet-control-scale,
.leaflet-container input,
.leaflet-container textarea,
.leaflet-container label,
.leaflet-container small {
font-size:12px;
line-height:20px;
}
.leaflet-container a {
color:#3887BE;
font-weight:normal;
text-decoration:none;
}
.leaflet-container a:hover { color:#63b6e5; }
.leaflet-container.dark a { color:#63b6e5; }
.leaflet-container.dark a:hover { color:#8fcaec; }
.leaflet-container.dark .mapbox-button,
.leaflet-container .mapbox-button {
background-color:#3887be;
display:inline-block;
height:40px;
line-height:40px;
text-decoration:none;
color:#fff;
font-size:12px;
white-space:nowrap;
text-overflow:ellipsis;
}
.leaflet-container.dark .mapbox-button:hover,
.leaflet-container .mapbox-button:hover {
color:#fff;
background-color:#3bb2d0;
}
/* Base Leaflet
------------------------------------------------------- */
.leaflet-map-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position:absolute;
left:0;
top:0;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-drag:none;
-webkit-user-select:none;
-moz-user-select:none;
user-select:none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
.leaflet-tile {
filter:inherit;
visibility:hidden;
}
.leaflet-tile-loaded {
visibility:inherit;
}
.leaflet-zoom-box {
width:0;
height:0;
}
.leaflet-tile-pane { z-index:2; }
.leaflet-objects-pane { z-index:3; }
.leaflet-overlay-pane { z-index:4; }
.leaflet-shadow-pane { z-index:5; }
.leaflet-marker-pane { z-index:6; }
.leaflet-popup-pane { z-index:7; }
.leaflet-control {
position:relative;
z-index:7;
pointer-events:auto;
float:left;
clear:both;
}
.leaflet-right .leaflet-control { float:right; }
.leaflet-top .leaflet-control { margin-top:10px; }
.leaflet-bottom .leaflet-control { margin-bottom:10px; }
.leaflet-left .leaflet-control { margin-left:10px; }
.leaflet-right .leaflet-control { margin-right:10px; }
.leaflet-top,
.leaflet-bottom {
position:absolute;
z-index:1000;
pointer-events:none;
}
.leaflet-top { top:0; }
.leaflet-right { right:0; }
.leaflet-bottom { bottom:0; }
.leaflet-left { left:0; }
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-popup {
opacity:0;
-webkit-transition:opacity 0.2s linear;
-moz-transition:opacity 0.2s linear;
-o-transition:opacity 0.2s linear;
transition:opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity:1;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition:-webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile,
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition:none;
-moz-transition:none;
-o-transition:none;
transition:none;
}
.leaflet-zoom-anim .leaflet-zoom-hide { visibility: hidden; }
/* cursors */
.map-clickable,
.leaflet-clickable {
cursor: pointer;
}
.leaflet-popup-pane,
.leaflet-control {
cursor:auto;
}
.leaflet-container {
cursor:-webkit-grab;
cursor: -moz-grab;
}
.leaflet-dragging,
.leaflet-dragging .map-clickable,
.leaflet-dragging .leaflet-clickable,
.leaflet-dragging .leaflet-container {
cursor:move;
cursor:-webkit-grabbing;
cursor: -moz-grabbing;
}
.leaflet-zoom-box {
background:#fff;
border:2px dotted #202020;
opacity:0.5;
}
/* general toolbar styles */
.leaflet-control-layers,
.leaflet-bar {
background-color:#fff;
border:1px solid #999;
border-color:rgba(0,0,0,0.4);
border-radius:3px;
box-shadow:none;
}
.leaflet-bar a,
.leaflet-bar a:hover {
color:#404040;
color:rgba(0,0,0,0.75);
border-bottom:1px solid #ddd;
border-bottom-color:rgba(0,0,0,0.10);
}
.leaflet-bar a:hover,
.leaflet-bar a:active {
background-color:#f8f8f8;
cursor:pointer;
}
.leaflet-bar a:first-child {
border-radius:3px 3px 0 0;
}
.leaflet-bar a:last-child {
border-bottom:none;
border-radius:0 0 3px 3px;
}
.leaflet-bar a:only-of-type {
border-radius:3px;
}
.leaflet-bar .leaflet-disabled {
cursor:default;
opacity:0.75;
}
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
display:block;
content:'';
text-indent:-999em;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display:none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display:block;
position:relative;
}
.leaflet-control-layers-expanded {
background:#fff;
padding:6px 10px 6px 6px;
color:#404040;
color:rgba(0,0,0,0.75);
}
.leaflet-control-layers-selector {
margin-top:2px;
position:relative;
top:1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height:0;
border-top:1px solid #ddd;
border-top-color:rgba(0,0,0,0.10);
margin:5px -10px 5px -6px;
}
.leaflet-container .leaflet-control-attribution {
background-color:rgba(255,255,255,0.25);
margin:0;
box-shadow:none;
}
.leaflet-control-attribution a:hover,
.map-info-container a:hover {
color:inherit;
text-decoration:underline;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding:0 5px;
}
.leaflet-left .leaflet-control-scale { margin-left:5px; }
.leaflet-bottom .leaflet-control-scale { margin-bottom:5px; }
.leaflet-control-scale-line {
background-color:rgba(255,255,255,0.5);
border:1px solid #999;
border-color:rgba(0,0,0,0.4);
border-top:none;
padding:2px 5px 1px;
white-space:nowrap;
overflow:hidden;
}
.leaflet-control-scale-line:not(:first-child) {
border-top:2px solid #ddd;
border-top-color:rgba(0,0,0,0.10);
border-bottom:none;
margin-top:-2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom:2px solid #777;
}
/* popup */
.leaflet-popup {
position:absolute;
text-align:center;
pointer-events:none;
}
.leaflet-popup-content-wrapper {
padding:1px;
text-align:left;
pointer-events:all;
}
.leaflet-popup-content {
padding:10px 10px 15px;
margin:0;
line-height:inherit;
}
.leaflet-popup-tip-container {
width:20px;
height:20px;
margin:0 auto;
position:relative;
}
.leaflet-popup-tip {
width:0;
height:0;
margin:0;
border-left:10px solid transparent;
border-right:10px solid transparent;
border-top:10px solid #fff;
box-shadow:none;
}
.leaflet-popup-close-button {
text-indent:-999em;
position:absolute;
top:0;right:0;
pointer-events:all;
}
.leaflet-popup-close-button:hover {
background-color:#f8f8f8;
}
.leaflet-popup-scrolled {
overflow:auto;
border-bottom:1px solid #ddd;
border-top:1px solid #ddd;
}
/* div icon */
.leaflet-div-icon {
background:#fff;
border:1px solid #999;
border-color:rgba(0,0,0,0.4);
}
.leaflet-editing-icon {
border-radius:3px;
}
/* Leaflet + Mapbox
------------------------------------------------------- */
.leaflet-bar a,
.mapbox-icon,
.map-tooltip.closable .close,
.leaflet-control-layers-toggle,
.leaflet-popup-close-button,
.mapbox-button-icon:before {
content:'';
display:inline-block;
width:26px;
height:26px;
vertical-align:middle;
background-repeat:no-repeat;
}
.leaflet-bar a {
display:block;
}
.leaflet-control-zoom-in,
.leaflet-control-zoom-out,
.leaflet-popup-close-button,
.leaflet-control-layers-toggle,
.leaflet-container.dark .map-tooltip .close,
.map-tooltip .close,
.mapbox-icon {
background-image:url(./images/icons-404040.png);
background-repeat:no-repeat;
background-size:26px 260px;
}
.mapbox-button-icon:before,
.leaflet-container.dark .leaflet-control-zoom-in,
.leaflet-container.dark .leaflet-control-zoom-out,
.leaflet-container.dark .leaflet-control-layers-toggle,
.leaflet-container.dark .mapbox-icon {
background-image:url(./images/icons-ffffff.png);
background-size:26px 260px;
}
.leaflet-bar .leaflet-control-zoom-in { background-position:0 0; }
.leaflet-bar .leaflet-control-zoom-out { background-position:0 -26px; }
.map-tooltip .close, .leaflet-popup-close-button { background-position:0 -52px; }
.mapbox-icon-info { background-position:0 -78px; }
.leaflet-control-layers-toggle { background-position:0 -104px; }
.mapbox-icon-share:before, .mapbox-icon-share { background-position:0 -130px; }
.mapbox-icon-geocoder:before, .mapbox-icon-geocoder { background-position:0 -156px; }
.mapbox-icon-facebook:before, .mapbox-icon-facebook { background-position:0 -182px; }
.mapbox-icon-twitter:before, .mapbox-icon-twitter { background-position:0 -208px; }
.mapbox-icon-pinterest:before, .mapbox-icon-pinterest { background-position:0 -234px; }
@media
(-webkit-min-device-pixel-ratio:2),
(min-resolution:192dpi) {
.leaflet-control-zoom-in,
.leaflet-control-zoom-out,
.leaflet-popup-close-button,
.leaflet-control-layers-toggle,
.mapbox-icon {
background-image:url(./images/icons-404040@2x.png);
}
.mapbox-button-icon:before,
.leaflet-container.dark .leaflet-control-zoom-in,
.leaflet-container.dark .leaflet-control-zoom-out,
.leaflet-container.dark .leaflet-control-layers-toggle,
.leaflet-container.dark .mapbox-icon {
background-image:url(./images/icons-ffffff@2x.png);
}
}
.leaflet-popup-content-wrapper,
.map-legends,
.map-tooltip {
background:#fff;
border-radius:3px;
box-shadow:0 1px 2px rgba(0,0,0,0.10);
}
.map-legends,
.map-tooltip {
max-width:300px;
}
.map-legends .map-legend {
padding:10px;
}
.map-tooltip {
z-index:999999;
padding:10px;
min-width:180px;
max-height:400px;
overflow:auto;
opacity:1;
-webkit-transition:opacity 150ms;
-moz-transition:opacity 150ms;
-o-transition:opacity 150ms;
transition:opacity 150ms;
}
.map-tooltip .close {
text-indent:-999em;
overflow:hidden;
display:none;
}
.map-tooltip.closable .close {
position:absolute;
top:0;right:0;
border-radius:3px;
}
.map-tooltip.closable .close:active {
background-color:#f8f8f8;
}
.leaflet-control-interaction {
position:absolute;
top:10px;
right:10px;
width:300px;
}
.leaflet-popup-content .marker-title {
font-weight:bold;
}
.leaflet-control .mapbox-button {
background-color:#fff;
border:1px solid #ddd;
border-color:rgba(0,0,0,0.10);
padding:5px 10px;
border-radius:3px;
}
/* Share modal
------------------------------------------------------- */
.mapbox-modal > div {
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
z-index:-1;
overflow-y:auto;
}
.mapbox-modal.active > div {
z-index:99999;
transition:all .2s, z-index 0 0;
}
.mapbox-modal .mapbox-modal-mask {
background:rgba(0,0,0,0.5);
opacity:0;
}
.mapbox-modal.active .mapbox-modal-mask { opacity:1; }
.mapbox-modal .mapbox-modal-content {
-webkit-transform:translateY(-100%);
-moz-transform:translateY(-100%);
-ms-transform:translateY(-100%);
transform:translateY(-100%);
}
.mapbox-modal.active .mapbox-modal-content {
-webkit-transform:translateY(0);
-moz-transform:translateY(0);
-ms-transform:translateY(0);
transform:translateY(0);
}
.mapbox-modal-body {
position:relative;
background:#fff;
padding:20px;
z-index:1000;
width:50%;
margin:20px 0 20px 25%;
}
.mapbox-share-buttons {
margin:0 0 20px;
}
.mapbox-share-buttons a {
width:33.3333%;
border-left:1px solid #fff;
text-align:center;
border-radius:0;
}
.mapbox-share-buttons a:last-child { border-radius:0 3px 3px 0; }
.mapbox-share-buttons a:first-child { border:none; border-radius:3px 0 0 3px; }
.mapbox-modal input {
width:100%;
height:40px;
padding:10px;
border:1px solid #ddd;
border-color:rgba(0,0,0,0.10);
color:rgba(0,0,0,0.5);
}
/* Info Control
------------------------------------------------------- */
.leaflet-control.mapbox-control-info {
margin:5px 30px 10px 10px;
min-height:26px;
}
.leaflet-control.mapbox-control-info-right {
margin:5px 10px 10px 30px;
}
.mapbox-info-toggle {
background-color:#fff;
background-color:rgba(255,255,255,0.25);
border-radius:50%;
position:absolute;
bottom:0;left:0;
z-index:1;
}
.mapbox-control-info-right .mapbox-info-toggle { left:auto; right:0; }
.mapbox-control-info.active .mapbox-info-toggle { background-color:#fff; }
.mapbox-info-toggle:hover { background-color:rgba(255,255,255,0.5); }
.map-info-container {
background:#fff;
background:rgba(255,255,255,0.75);
padding:3px 5px 3px 15px;
display:none;
position:relative;
bottom:0;
left:13px;
border-radius:3px;
}
.mapbox-control-info.active .map-info-container { display:inline-block; }
.mapbox-control-info-right .map-info-container {
left:auto;
right:13px;
padding:3px 15px 3px 5px;
}
/* Geocoder
------------------------------------------------------- */
.leaflet-control-mapbox-geocoder {
position:relative;
}
.leaflet-control-mapbox-geocoder.searching {
opacity:0.75;
}
.leaflet-control-mapbox-geocoder .leaflet-control-mapbox-geocoder-wrap {
background:#fff;
position:absolute;
border:1px solid #999;
border-color:rgba(0,0,0,0.4);
border-bottom-width:0;
overflow:hidden;
left:26px;
height:27px;
width:0;
top:-1px;
border-radius:0 3px 3px 0;
opacity:0;
-webkit-transition:opacity 100ms;
-moz-transition:opacity 100ms;
-o-transition:opacity 100ms;
transition:opacity 100ms;
}
.leaflet-control-mapbox-geocoder.active .leaflet-control-mapbox-geocoder-wrap {
width:180px;
opacity:1;
}
.leaflet-bar .leaflet-control-mapbox-geocoder-toggle,
.leaflet-bar .leaflet-control-mapbox-geocoder-toggle:hover {
border-bottom:none;
}
.leaflet-control-mapbox-geocoder-toggle {
border-radius:3px;
}
.leaflet-control-mapbox-geocoder.active,
.leaflet-control-mapbox-geocoder.active .leaflet-control-mapbox-geocoder-toggle {
border-top-right-radius:0;
border-bottom-right-radius:0;
}
.leaflet-control-mapbox-geocoder .leaflet-control-mapbox-geocoder-form input {
background:transparent;
border:0;
width:180px;
padding:0 0 0 10px;
height:26px;
outline:none;
}
.leaflet-control-mapbox-geocoder-results {
width:180px;
position:absolute;
left:26px;
top:25px;
border-radius:0 0 3px 3px;
}
.leaflet-control-mapbox-geocoder.active .leaflet-control-mapbox-geocoder-results {
background:#fff;
border:1px solid #999;
border-color:rgba(0,0,0,0.4);
}
.leaflet-control-mapbox-geocoder-results a,
.leaflet-control-mapbox-geocoder-results span {
padding:0 10px;
text-overflow:ellipsis;
white-space:nowrap;
display:block;
width:100%;
font-size:12px;
line-height:26px;
text-align:left;
overflow:hidden;
}
.leaflet-control-mapbox-geocoder-results a:first-child {
border-top:1px solid #999;
border-top-color:rgba(0,0,0,0.4);
border-radius:0;
}
.leaflet-container.dark .leaflet-control .leaflet-control-mapbox-geocoder-results a:hover,
.leaflet-control-mapbox-geocoder-results a:hover {
background:#f8f8f8;
opacity:1;
}
/* Dark Theme
------------------------------------------------------- */
.leaflet-container.dark .leaflet-bar {
background-color:#404040;
border-color:#202020;
border-color:rgba(0,0,0,0.75);
}
.leaflet-container.dark .leaflet-bar a {
color:#404040;
border-color:rgba(0,0,0,0.5);
}
.leaflet-container.dark .leaflet-bar a:active,
.leaflet-container.dark .leaflet-bar a:hover {
background-color:#505050;
}
.leaflet-container.dark .mapbox-info-toggle,
.leaflet-container.dark .map-info-container,
.leaflet-container.dark .leaflet-control-attribution {
background-color:rgba(0,0,0,0.25);
color:#f8f8f8;
}
.leaflet-container.dark .leaflet-bar a.leaflet-disabled,
.leaflet-container.dark .leaflet-control .mapbox-button.disabled {
background-color:#252525;
color:#404040;
}
.leaflet-container.dark .leaflet-control-mapbox-geocoder > div {
border-color:#202020;
border-color:rgba(0,0,0,0.75);
}
.leaflet-container.dark .leaflet-control .leaflet-control-mapbox-geocoder-results a {
border-color:#ddd #202020;
border-color:rgba(0,0,0,0.10) rgba(0,0,0,0.75);
}
.leaflet-container.dark .leaflet-control .leaflet-control-mapbox-geocoder-results span {
border-color:#202020;
border-color:rgba(0,0,0,0.75);
}
/* Larger Screens
------------------------------------------------------- */
@media only screen and (max-width:800px) {
.mapbox-modal-body {
width:83.3333%;
margin-left:8.3333%;
}
}
/* Smaller Screens
------------------------------------------------------- */
@media only screen and (max-width:640px) {
.mapbox-modal-body {
width:100%;
height:100%;
margin:0;
}
}
/* Browser Fixes
------------------------------------------------------- */
/* Map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img { max-width:none!important; }
/* Stupid Android 2 doesn't understand "max-width: none" properly */
.leaflet-container img.leaflet-image-layer { max-width:15000px!important; }
/* Android chrome makes tiles disappear without this */
.leaflet-tile-container img { -webkit-backface-visibility:hidden; }
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg { -moz-user-select:none; }
/* Older IEs don't support the translateY property for display animation */
.leaflet-oldie .mapbox-modal .mapbox-modal-content { display:none; }
.leaflet-oldie .mapbox-modal.active .mapbox-modal-content { display:block; }
.map-tooltip { width:280px\8; /* < IE9 */ }

View File

File diff suppressed because one or more lines are too long

241
gui-src/css/styles.css Normal file
View File

@@ -0,0 +1,241 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
p {
color: #ececec;
}
.container {
margin: 0;
padding-top: 1vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: 0.75s;
}
.logo.arnis:hover {
filter: drop-shadow(0 0 2em #b3b3b3);
}
.row {
display: flex;
justify-content: center;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
.flex-container {
display: flex;
gap: 20px;
justify-content: center;
align-items: stretch; /* Ensures both sections take full height */
margin-top: 5px;
}
.section {
background: #575757;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.map-box,
.controls-box {
width: 45%;
background: #575757;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
/* No display: flex here, so buttons and content aren't stretched */
}
.controls-content {
display: flex;
flex-direction: column;
height: 100%;
}
.controls-box .progress-section {
margin-top: auto; /* Keeps the progress section at the bottom */
}
.map-container {
border: 2px solid #e0e0e0;
border-radius: 8px;
}
.section h2 {
margin-top: 0;
margin-bottom: 10px;
}
p {
color: #d6d6d6;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
color: #0f0f0f;
background-color: #ffffff;
cursor: pointer;
transition: border-color 0.25s;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
margin-top: 10px;
width: auto; /* Ensures buttons dont stretch */
}
button:hover {
border-color: #396cd8;
}
#selected-directory {
font-size: 1em;
margin-top: 6px;
}
.progress-section {
margin-top: auto;
}
.progress-section h2 {
margin-bottom: 8px;
text-align: center;
}
.progress-bar-container {
width: 100%;
height: 20px;
background-color: #e0e0e0;
border-radius: 10px;
overflow: hidden;
margin-top: 8px;
}
.progress-bar {
height: 100%;
width: 0%;
background-color: #4caf50;
transition: width 0.4s;
}
/* Left and right alignment for "Saving world..." text */
.progress-status {
display: flex;
justify-content: space-between;
font-size: 0.9em;
margin-top: 8px;
color: #fff;
}
.footer {
margin-top: 20px;
text-align: center;
font-size: 0.9em;
}
.footer-link {
color: #ffffff;
text-decoration: none;
}
.footer-link:hover {
color: #b3b3b3;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
p {
color: #ececec;
}
input,
button {
color: #ffffff;
background-color: #0f0f0f98;
}
button:active {
background-color: #0f0f0f69;
}
}
.tooltip {
position: relative;
display: block;
width: 100%;
}
.tooltip button {
width: 100%;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 90%;
background-color: #808080;
color: #fff;
text-align: center;
padding: 5px 0;
border-radius: 6px;
position: absolute;
z-index: 1;
bottom: -100%;
left: 5%;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip .tooltiptext::after {
content: "";
position: absolute;
top: -10px;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #808080 transparent;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.controls-box button {
width: 100%;
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

BIN
gui-src/images/logo.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
gui-src/images/tile.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

80
gui-src/index.html Normal file
View File

@@ -0,0 +1,80 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="./css/styles.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Arnis</title>
<script type="module" src="./js/main.js" defer></script>
</head>
<body>
<main class="container">
<div class="row">
<a href="https://github.com/louis-e/arnis" target="_blank">
<img src="./images/logo.png" class="logo arnis" alt="Arnis Logo" />
</a>
</div>
<div class="flex-container">
<!-- Left Box: Map and BBox Input -->
<section class="section map-box" style="margin-bottom: 0; padding-bottom: 0;">
<h2>Select Location</h2>
<span id="bbox-text" style="font-size: 1.0em; display: block; margin-top: -8px; margin-bottom: 3px;">
Zoom in and choose your area using the rectangle tool
</span>
<iframe src="maps.html" width="100%" height="300" class="map-container" title="Map Picker"></iframe>
<span id="bbox-info"
style="font-size: 0.75em; color: #7bd864; display: block; margin-bottom: 4px; font-weight: bold; min-height: 2em;"></span>
</section>
<!-- Right Box: Directory Selection, Start Button, and Progress Bar -->
<section class="section controls-box">
<div class="controls-content">
<h2>Select World</h2>
<!-- Updated Tooltip Structure -->
<div class="tooltip" style="width: 100%;">
<button type="button" onclick="pickDirectory()" style="padding: 10px; line-height: 1.2; width: 100%;">
Choose World
<br>
<span id="selected-world" style="font-size: 0.8em; color: #fecc44; display: block; margin-top: 4px;">
No world selected
</span>
</button>
<span class="tooltiptext" style="font-size: 0.8em; line-height: 1.2;">
Please select a Minecraft world that can be overwritten, as the generation process will replace existing structures in the chosen world!
</span>
</div>
<br>
<button type="button" onclick="startGeneration()">Start Generation</button>
<br><br>
<div class="progress-section">
<h2>Progress</h2>
<div class="progress-bar-container">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div class="progress-status">
<span id="progress-message"></span>
<span id="progress-detail">0%</span>
</div>
</div>
</div>
</section>
</div>
<!-- Footer -->
<footer class="footer">
<a href="https://github.com/louis-e/arnis" target="_blank" class="footer-link">
© <span id="current-year"></span> Arnis v<span id="version-placeholder">x.x.x</span> by louis-e
</a>
</footer>
</main>
</body>
</html>

657
gui-src/js/bbox.js Normal file
View File

@@ -0,0 +1,657 @@
var map, rsidebar, lsidebar, drawControl, drawnItems = null;
// Where we keep the big list of proj defs from the server
var proj4defs = null;
// Where we keep the proj objects we are using in this session
var projdefs = { "4326": L.CRS.EPSG4326, "3857": L.CRS.EPSG3857 };
var currentproj = "3857";
var currentmouse = L.latLng(0, 0);
/*
**
** override L.Rectangle
** to fire an event after setting
**
** the base parent object L.Path
** includes the L.Mixin.Events
**
** ensures bbox box is always
** the topmost SVG feature
**
*/
L.Rectangle.prototype.setBounds = function (latLngBounds) {
this.setLatLngs(this._boundsToLatLngs(latLngBounds));
this.fire('bounds-set');
}
var FormatSniffer = (function () { // execute immediately
'use strict';
/*
**
** constructor
**
*/
var FormatSniffer = function (options) {
options || (options = {});
if (!this || !(this instanceof FormatSniffer)) {
return new FormatSniffer(options);
}
this.regExes = {
ogrinfoExtent: /Extent\:\s\((.*)\)/,
bbox: /^\(([\s|\-|0-9]*\.[0-9]*,[\s|\-|0-9]*\.[0-9]*,[\s|\-|0-9]*\.[0-9]*,[\s|\-|0-9]*\.[0-9|\s]*)\)$/
};
this.data = options.data || "";
this.parse_type = null;
};
/*
**
** functions
**
*/
FormatSniffer.prototype.sniff = function () {
return this._sniffFormat();
};
FormatSniffer.prototype._is_ogrinfo = function () {
var match = this.regExes.ogrinfoExtent.exec(this.data.trim());
var extent = [];
if (match) {
var pairs = match[1].split(") - (");
for (var indx = 0; indx < pairs.length; indx++) {
var coords = pairs[indx].trim().split(",");
extent = (extent.concat([parseFloat(coords[0].trim()), parseFloat(coords[1].trim())]));
}
}
this.parse_type = "ogrinfo";
return extent;
};
FormatSniffer.prototype._is_normal_bbox = function () {
var match = this.regExes.bbox.exec(this.data.trim());
var extent = [];
if (match) {
var bbox = match[1].split(",");
for (var indx = 0; indx < bbox.length; indx++) {
var coord = bbox[indx].trim();
extent = (extent.concat([parseFloat(coord)]));
}
}
this.parse_type = "bbox";
return extent;
};
FormatSniffer.prototype._is_geojson = function () {
try {
// try JSON
var json = JSON.parse(this.data);
// try GeoJSON
var parsed_data = new L.geoJson(json)
} catch (err) {
return null;
}
this.parse_type = "geojson";
return parsed_data;
};
FormatSniffer.prototype._is_wkt = function () {
if (this.data === "") {
throw new Error("empty -- nothing to parse");
}
try {
var parsed_data = new Wkt.Wkt(this.data);
} catch (err) {
return null;
}
this.parse_type = "wkt";
return parsed_data;
};
FormatSniffer.prototype._sniffFormat = function () {
var parsed_data = null;
var fail = false;
try {
var next = true;
// try ogrinfo
parsed_data = this._is_ogrinfo()
if (parsed_data.length > 0) {
next = false;
}
// try normal bbox
if (next) {
parsed_data = this._is_normal_bbox();
if (parsed_data.length > 0) next = false;
}
// try GeoJSON
if (next) {
parsed_data = this._is_geojson();
if (parsed_data) next = false;
}
// try WKT
if (next) {
parsed_data = this._is_wkt();
if (parsed_data) next = false;
}
// no matches, throw error
if (next) {
fail = true;
/*
** sorry, this block needs to be left aligned
** to make the alert more readable
** which means, we probably shouldn't use alerts ;-)
*/
throw {
"name": "NoTypeMatchError",
"message": "The data is not a recognized format:\n \
1. ogrinfo extent output\n \
2. bbox as (xMin,yMin,xMax,yMax )\n \
3. GeoJSON\n \
4. WKT\n\n "
}
}
} catch (err) {
alert("Your paste is not parsable:\n" + err.message);
fail = true;
}
// delegate to format handler
if (!fail) {
this._formatHandler[this.parse_type].call(this._formatHandler, parsed_data);
}
return (fail ? false : true);
};
/*
** an object with functions as property names.
** if we need to add another format
** we can do so here as a property name
** to enforce reusability
**
** to add different formats as L.FeatureGroup layer
** so they work with L.Draw edit and delete options
** we fake passing event information
** and triggering draw:created for L.Draw
*/
FormatSniffer.prototype._formatHandler = {
// coerce event objects to work with L.Draw types
coerce: function (lyr, type_obj) {
var event_obj = {
layer: lyr,
layerType: null,
}
// coerce to L.Draw types
if (/point/i.test(type_obj)) {
event_obj.layerType = "marker";
}
else if (/linestring/i.test(type_obj)) {
event_obj.layerType = "polyline";
}
else if (/polygon/i.test(type_obj)) {
event_obj.layerType = "polygon";
}
return event_obj;
},
reduce_layers: function (lyr) {
var lyr_parts = [];
if (typeof lyr['getLayers'] === 'undefined') {
return [lyr];
}
else {
var all_layers = lyr.getLayers();
for (var i = 0; i < all_layers.length; i++) {
lyr_parts = lyr_parts.concat(this.reduce_layers(all_layers[i]));
}
}
return lyr_parts;
},
get_leaflet_bounds: function (data) {
/*
** data comes in an extent ( xMin,yMin,xMax,yMax )
** we need to swap lat/lng positions
** because leaflet likes it hard
*/
var sw = [data[1], data[0]];
var ne = [data[3], data[2]];
return new L.LatLngBounds(sw, ne);
},
wkt: function (data) {
var wkt_layer = data.construct[data.type].call(data);
var all_layers = this.reduce_layers(wkt_layer);
for (var indx = 0; indx < all_layers.length; indx++) {
var lyr = all_layers[indx];
var evt = this.coerce(lyr, data.type);
// call L.Draw.Feature.prototype._fireCreatedEvent
map.fire('draw:created', evt);
}
},
geojson: function (geojson_layer) {
var all_layers = this.reduce_layers(geojson_layer);
for (var indx = 0; indx < all_layers.length; indx++) {
var lyr = all_layers[indx];
var geom_type = geojson_layer.getLayers()[0].feature.geometry.type;
var evt = this.coerce(lyr, geom_type);
// call L.Draw.Feature.prototype._fireCreatedEvent
map.fire('draw:created', evt);
}
},
ogrinfo: function (data) {
var lBounds = this.get_leaflet_bounds(data);
// create a rectangle layer
var lyr = new L.Rectangle(lBounds);
var evt = this.coerce(lyr, 'polygon');
// call L.Draw.Feature.prototype._fireCreatedEvent
map.fire('draw:created', evt);
},
bbox: function (data) {
var lBounds = this.get_leaflet_bounds(data);
// create a rectangle layer
var lyr = new L.Rectangle(lBounds);
var evt = this.coerce(lyr, 'polygon');
// call L.Draw.Feature.prototype._fireCreatedEvent
map.fire('draw:created', evt);
}
};
return FormatSniffer; // return class def
})(); // end FormatSniffer
function addLayer(layer, name, title, zIndex, on) {
if (on) {
layer.setZIndex(zIndex).addTo(map);
} else {
layer.setZIndex(zIndex);
}
// Create a simple layer switcher that toggles layers on and off.
var ui = document.getElementById('map-ui');
var item = document.createElement('li');
var link = document.createElement('a');
link.href = '#';
if (on) {
link.className = 'enabled';
} else {
link.className = '';
}
link.innerHTML = name;
link.title = title;
link.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
if (map.hasLayer(layer)) {
map.removeLayer(layer);
this.className = '';
} else {
map.addLayer(layer);
this.className = 'enabled';
}
};
item.appendChild(link);
ui.appendChild(item);
};
function formatBounds(bounds, proj) {
var gdal = $("input[name='gdal-checkbox']").prop('checked');
var lngLat = $("input[name='coord-order']").prop('checked');
var formattedBounds = '';
var southwest = bounds.getSouthWest();
var northeast = bounds.getNorthEast();
var xmin = 0;
var ymin = 0;
var xmax = 0;
var ymax = 0;
if (proj == '4326') {
xmin = southwest.lng.toFixed(6);
ymin = southwest.lat.toFixed(6);
xmax = northeast.lng.toFixed(6);
ymax = northeast.lat.toFixed(6);
} else {
var proj_to_use = null;
if (typeof (projdefs[proj]) !== 'undefined') {
// we have it already, then grab it and use it...
proj_to_use = projdefs[proj];
} else {
// We have not used this one yet... make it and store it...
projdefs[proj] = new L.Proj.CRS(proj, proj4defs[proj][1]);
proj_to_use = projdefs[proj];
}
southwest = proj_to_use.project(southwest)
northeast = proj_to_use.project(northeast)
xmin = southwest.x.toFixed(4);
ymin = southwest.y.toFixed(4);
xmax = northeast.x.toFixed(4);
ymax = northeast.y.toFixed(4);
}
if (gdal) {
if (lngLat) {
formattedBounds = xmin + ',' + ymin + ',' + xmax + ',' + ymax;
} else {
formattedBounds = ymin + ',' + xmin + ',' + ymax + ',' + xmax;
}
} else {
if (lngLat) {
formattedBounds = xmin + ' ' + ymin + ' ' + xmax + ' ' + ymax;
} else {
formattedBounds = ymin + ' ' + xmin + ' ' + ymax + ' ' + xmax;
}
}
return formattedBounds
}
function formatTile(point, zoom) {
var xTile = Math.floor((point.lng + 180) / 360 * Math.pow(2, zoom));
var yTile = Math.floor((1 - Math.log(Math.tan(point.lat * Math.PI / 180) + 1 / Math.cos(point.lat * Math.PI / 180)) / Math.PI) / 2 * Math.pow(2, zoom));
return xTile.toString() + ',' + yTile.toString();
}
function formatPoint(point, proj) {
var gdal = $("input[name='gdal-checkbox']").prop('checked');
var lngLat = $("input[name='coord-order']").prop('checked');
var formattedPoint = '';
if (proj == '4326') {
x = point.lng.toFixed(6);
y = point.lat.toFixed(6);
} else {
var proj_to_use = null;
if (typeof (projdefs[proj]) !== 'undefined') {
// we have it already, then grab it and use it...
proj_to_use = projdefs[proj];
} else {
// We have not used this one yet... make it and store it...
projdefs[proj] = new L.Proj.CRS(proj, proj4defs[proj][1]);
proj_to_use = projdefs[proj];
}
point = proj_to_use.project(point)
x = point.x.toFixed(4);
y = point.y.toFixed(4);
}
if (gdal) {
if (lngLat) {
formattedBounds = x + ',' + y;
} else {
formattedBounds = y + ',' + x;
}
} else {
if (lngLat) {
formattedBounds = x + ' ' + y;
} else {
formattedBounds = y + ' ' + x;
}
}
return formattedBounds
}
function validateStringAsBounds(bounds) {
var splitBounds = bounds ? bounds.split(',') : null;
return ((splitBounds !== null) &&
(splitBounds.length == 4) &&
((-90.0 <= parseFloat(splitBounds[0]) <= 90.0) &&
(-180.0 <= parseFloat(splitBounds[1]) <= 180.0) &&
(-90.0 <= parseFloat(splitBounds[2]) <= 90.0) &&
(-180.0 <= parseFloat(splitBounds[3]) <= 180.0)) &&
(parseFloat(splitBounds[0]) < parseFloat(splitBounds[2]) &&
parseFloat(splitBounds[1]) < parseFloat(splitBounds[3])))
}
$(document).ready(function () {
/*
**
** make sure all textarea inputs
** are selected once they are clicked
** because some people might not
** have flash enabled or installed
** and yes...
** there's a fucking Flash movie floating
** on top of your DOM
**
*/
$('input[type="textarea"]').on('click', function (evt) { this.select() });
// Have to init the projection input box as it is used to format the initial values
$("#projection").val(currentproj);
L.mapbox.accessToken = 'pk.eyJ1IjoiY3Vnb3MiLCJhIjoiY2p4Nm43MzA3MDFmZDQwcGxsMjB4Z3hnNiJ9.SQbnMASwdqZe6G4n6OMvVw';
map = L.mapbox.map('map').setView([50.114768, 8.687322], 4).addLayer(L.mapbox.styleLayer('mapbox://styles/mapbox/streets-v11'));
rsidebar = L.control.sidebar('rsidebar', {
position: 'right',
closeButton: true
});
rsidebar.on("sidebar-show", function (e) {
$("#map .leaflet-tile-loaded").addClass("blurred");
});
rsidebar.on("sidebar-hide", function (e) {
$('#map .leaflet-tile-loaded').removeClass('blurred');
$('#map .leaflet-tile-loaded').addClass('unblurred');
setTimeout(function () {
$('#map .leaflet-tile-loaded').removeClass('unblurred');
}, 7000);
});
map.addControl(rsidebar);
lsidebar = L.control.sidebar('lsidebar', {
position: 'left'
});
map.addControl(lsidebar);
// Add in a crosshair for the map
var crosshairIcon = L.icon({
iconUrl: 'images/crosshair.png',
iconSize: [20, 20], // size of the icon
iconAnchor: [10, 10], // point of the icon which will correspond to marker's location
});
crosshair = new L.marker(map.getCenter(), { icon: crosshairIcon, clickable: false });
crosshair.addTo(map);
// Initialize the FeatureGroup to store editable layers
drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Initialize the draw control and pass it the FeatureGroup of editable layers
drawControl = new L.Control.Draw({
edit: {
featureGroup: drawnItems
},
draw: {
polyline: false,
polygon: false,
circle: false,
marker: false
}
});
map.addControl(drawControl);
/*
**
** create bounds layer
** and default it at first
** to draw on null island
** so it's not seen onload
**
*/
startBounds = new L.LatLngBounds([0.0, 0.0], [0.0, 0.0]);
var bounds = new L.Rectangle(startBounds,
{
fill: false,
opacity: 1.0,
color: '#000'
}
);
bounds.on('bounds-set', function (e) {
// move it to the end of the parent
var parent = e.target._renderer._container.parentElement;
$(parent).append(e.target._renderer._container);
// Set the hash
var southwest = this.getBounds().getSouthWest();
var northeast = this.getBounds().getNorthEast();
var xmin = southwest.lng.toFixed(6);
var ymin = southwest.lat.toFixed(6);
var xmax = northeast.lng.toFixed(6);
var ymax = northeast.lat.toFixed(6);
location.hash = ymin + ',' + xmin + ',' + ymax + ',' + xmax;
});
map.addLayer(bounds)
map.on('draw:created', function (e) {
drawnItems.addLayer(e.layer);
bounds.setBounds(drawnItems.getBounds())
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));
notifyBboxUpdate();
if (!e.geojson &&
!((drawnItems.getLayers().length == 1) && (drawnItems.getLayers()[0] instanceof L.Marker))) {
map.fitBounds(bounds.getBounds());
} else {
if ((drawnItems.getLayers().length == 1) && (drawnItems.getLayers()[0] instanceof L.Marker)) {
map.panTo(drawnItems.getLayers()[0].getLatLng());
}
}
});
map.on('draw:deleted', function (e) {
e.layers.eachLayer(function (l) {
drawnItems.removeLayer(l);
});
if (drawnItems.getLayers().length > 0 &&
!((drawnItems.getLayers().length == 1) && (drawnItems.getLayers()[0] instanceof L.Marker))) {
bounds.setBounds(drawnItems.getBounds())
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));
notifyBboxUpdate();
map.fitBounds(bounds.getBounds());
} else {
bounds.setBounds(new L.LatLngBounds([0.0, 0.0], [0.0, 0.0]));
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));
notifyBboxUpdate();
if (drawnItems.getLayers().length == 1) {
map.panTo(drawnItems.getLayers()[0].getLatLng());
}
}
});
map.on('draw:edited', function (e) {
bounds.setBounds(drawnItems.getBounds())
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));
notifyBboxUpdate();
map.fitBounds(bounds.getBounds());
});
function display() {
$('#boxbounds').text(formatBounds(bounds.getBounds(), '4326'));
$('#boxboundsmerc').text(formatBounds(bounds.getBounds(), currentproj));
notifyBboxUpdate();
}
display();
map.on('move', function (e) {
crosshair.setLatLng(map.getCenter());
});
// handle geolocation click events
$('#geolocation').click(function () {
map.locate({ setView: true, maxZoom: 8 });
$('#geolocation a').toggleClass('active');
$('#geolocation a').toggleClass('active', 350);
});
$('button#add').on('click', function (evt) {
var sniffer = FormatSniffer({ data: $('div#rsidebar textarea').val() });
var is_valid = sniffer.sniff();
if (is_valid) {
rsidebar.hide();
$('#create-geojson a').toggleClass('enabled');
map.fitBounds(bounds.getBounds());
}
});
$('button#clear').on('click', function (evt) {
$('div#rsidebar textarea').val('');
});
var initialBBox = location.hash ? location.hash.replace(/^#/, '') : null;
if (initialBBox) {
if (validateStringAsBounds(initialBBox)) {
var splitBounds = initialBBox.split(',');
startBounds = new L.LatLngBounds([splitBounds[0], splitBounds[1]],
[splitBounds[2], splitBounds[3]]);
var lyr = new L.Rectangle(startBounds);
var evt = {
layer: lyr,
layerType: "polygon",
}
map.fire('draw:created', evt);
//map.fitBounds(bounds.getBounds());
} else {
// This will reset the hash if the original hash was not valid
bounds.setBounds(bounds.getBounds());
}
} else {
// Initially set the hash if there was not one set by the user
bounds.setBounds(bounds.getBounds());
}
$("input").click(function (e) {
display();
});
});
function notifyBboxUpdate() {
const bboxText = document.getElementById('boxbounds').textContent;
window.parent.postMessage({ bboxText: bboxText }, '*');
}

9597
gui-src/js/libs/jquery-1.9.1.js vendored Normal file
View File

File diff suppressed because it is too large Load Diff

5
gui-src/js/libs/jquery-1.9.1.min.js vendored Normal file
View File

File diff suppressed because one or more lines are too long

14971
gui-src/js/libs/jquery-ui-1.10.3.custom.js vendored Normal file
View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

170
gui-src/js/main.js Normal file
View File

@@ -0,0 +1,170 @@
const { invoke } = window.__TAURI__.core;
// Initialize elements and start the demo progress
window.addEventListener("DOMContentLoaded", async () => {
const currentYear = new Date().getFullYear();
document.getElementById("current-year").textContent = currentYear;
// Update displayed version
try {
const version = await invoke('gui_get_version');
const footerLink = document.querySelector(".footer-link");
footerLink.textContent = `© ${currentYear} Arnis v${version} by louis-e`;
} catch (error) {
console.error("Failed to fetch version:", error);
}
// Check for updates
try {
const isUpdateAvailable = await invoke('gui_check_for_updates');
if (isUpdateAvailable) {
const footer = document.querySelector(".footer");
const updateMessage = document.createElement("a");
updateMessage.href = "https://github.com/louis-e/arnis/releases";
updateMessage.target = "_blank";
updateMessage.style.color = "#fecc44";
updateMessage.style.marginTop = "-5px";
updateMessage.style.fontSize = "0.95em";
updateMessage.style.display = "block";
updateMessage.style.textDecoration = "none";
updateMessage.textContent = "There's a new version available! Click here to download it.";
footer.style.marginTop = "15px";
footer.appendChild(updateMessage);
}
} catch (error) {
console.error("Failed to check for updates: ", error);
}
// Register bbox update event for iframe map
window.addEventListener('message', function (event) {
const bboxText = event.data.bboxText;
if (bboxText) {
console.log("Updated BBOX Coordinates:", bboxText);
displayBboxInfoText(bboxText);
}
});
window.pickDirectory = pickDirectory;
window.startGeneration = startGeneration;
const progressBar = document.getElementById("progress-bar");
const progressMessage = document.getElementById("progress-message");
const progressDetail = document.getElementById("progress-detail");
// Listen for progress-update events
window.__TAURI__.event.listen("progress-update", (event) => {
const { progress, message } = event.payload;
if (progress != -1) {
progressBar.style.width = `${progress}%`;
progressDetail.textContent = `${Math.round(progress)}%`;
}
if (message != "") {
progressMessage.textContent = message;
if (message.startsWith("Error!")) {
progressMessage.style.color = "#fa7878";
} else {
progressMessage.style.color = "";
}
}
});
});
// Function to calculate the bounding box "size" in square meters based on latitude and longitude
function calculateBBoxSize(lng1, lat1, lng2, lat2) {
// Approximate distance calculation using Haversine formula or geodesic formula
const toRad = (angle) => (angle * Math.PI) / 180;
const R = 6371000; // Earth radius in meters
const latDistance = toRad(lat2 - lat1);
const lngDistance = toRad(lng2 - lng1);
const a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
// Width and height of the box
const height = R * latDistance;
const width = R * lngDistance;
return Math.abs(width * height);
}
const threshold1 = 12332660.00;
const threshold2 = 36084700.00;
let selectedBBox = "";
// Function to handle incoming bbox data
function displayBboxInfoText(bboxText) {
selectedBBox = bboxText;
const [lng1, lat1, lng2, lat2] = bboxText.split(" ").map(Number);
const bboxInfo = document.getElementById("bbox-info");
// Reset the info text if the bbox is 0,0,0,0
if (lng1 === 0 && lat1 === 0 && lng2 === 0 && lat2 === 0) {
bboxInfo.textContent = "";
return;
}
// Calculate the size of the selected bbox
const selectedSize = calculateBBoxSize(lng1, lat1, lng2, lat2);
if (selectedSize > threshold2) {
bboxInfo.textContent = "This area is very large and could exceed typical computing limits.";
bboxInfo.style.color = "#fa7878";
} else if (selectedSize > threshold1) {
bboxInfo.textContent = "The area is quite extensive and may take significant time and resources.";
bboxInfo.style.color = "#fecc44";
} else {
bboxInfo.textContent = "Selection confirmed!";
bboxInfo.style.color = "#7bd864";
}
}
let worldPath = "";
async function pickDirectory() {
try {
const worldName = await invoke('gui_pick_directory');
if (worldName) {
worldPath = worldName;
const lastSegment = worldName.split(/[\\/]/).pop();
document.getElementById('selected-world').textContent = lastSegment;
document.getElementById('selected-world').style.color = "#fecc44";
}
} catch (error) {
console.error(error);
document.getElementById('selected-world').textContent = error;
document.getElementById('selected-world').style.color = "#fa7878";
}
}
async function startGeneration() {
try {
if (worldPath === "No world selected" || worldPath == "Invalid Minecraft world" || worldPath === "") {
document.getElementById('selected-world').textContent = "Select a Minecraft world first!";
document.getElementById('selected-world').style.color = "#fa7878";
return;
}
if (!selectedBBox || selectedBBox == "0.000000 0.000000 0.000000 0.000000") {
document.getElementById('bbox-info').textContent = "Select a location firsta using the rectangle tool!";
document.getElementById('bbox-info').style.color = "#fa7878";
return;
}
// Pass the bounding box and selected world to the Rust backend
await invoke("gui_start_generation", { bboxText: selectedBBox, selectedWorld: worldPath });
// Update the UI or show a loading/progress message if needed
console.log("Generation process started.");
} catch (error) {
console.error("Error starting generation:", error);
}
}

View File

File diff suppressed because it is too large Load Diff

2766
gui-src/js/maps/leaflet.draw-src.js vendored Normal file
View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,122 @@
L.Control.Sidebar = L.Control.extend({
includes: L.Mixin.Events,
options: {
closeButton: true,
position: 'left'
},
initialize: function (placeholder, options) {
L.setOptions(this, options);
// Find content container
var content = this._contentContainer = L.DomUtil.get(placeholder);
// Remove the content container from its original parent
content.parentNode.removeChild(content);
// Make sure we don't drag the map when we interact with the content
var stop = L.DomEvent.stopPropagation;
L.DomEvent
.on(content, 'click', stop)
.on(content, 'mousedown', stop)
.on(content, 'touchstart', stop)
.on(content, 'dblclick', stop)
.on(content, 'mousewheel', stop)
.on(content, 'MozMousePixelScroll', stop);
},
addTo: function (map) {
var sidebar = this;
var l = 'leaflet-';
// Create sidebar container
var container = this._container =
L.DomUtil.create('div', l + 'sidebar ' + this.options.position);
// Style and attach content container
var content = this._contentContainer;
L.DomUtil.addClass(content, l + 'control');
container.appendChild(content);
// Create close button and attach it if configured
if (this.options.closeButton) {
var close = L.DomUtil.create('a', 'close', container);
close.innerHTML = '&times;';
L.DomEvent.on(close, 'click', function (e) {
sidebar.hide();
L.DomEvent.stopPropagation(e);
});
}
// Attach sidebar container to controls container
var controlContainer = map._controlContainer;
controlContainer.insertBefore(container, controlContainer.firstChild);
this._map = map;
return this;
},
removeFrom: function (map) {
// Remove sidebar container from controls container
var controlContainer = map._controlContainer;
controlContainer.removeChild(this._container);
return this;
},
isVisible: function () {
return L.DomUtil.hasClass(this._container, 'visible');
},
show: function () {
if (!this.isVisible()) {
L.DomUtil.addClass(this._container, 'visible');
//this._map.panBy([-this.getOffset() / 2, 0], {
// duration: 0.5
//});
this.fire("sidebar-show");
}
},
hide: function () {
if (this.isVisible()) {
L.DomUtil.removeClass(this._container, 'visible');
//this._map.panBy([this.getOffset() / 2, 0], {
// duration: 0.5
//});
this.fire("sidebar-hide");
}
},
toggle: function () {
if (this.isVisible()) {
this.hide();
} else {
this.show();
}
},
getContainer: function () {
return this._contentContainer;
},
setContent: function (content) {
this.getContainer().innerHTML = content;
return this;
},
getOffset: function () {
if (this.options.position === 'right') {
return -this._container.offsetWidth;
} else {
return this._container.offsetWidth;
}
}
});
L.control.sidebar = function (placeholder, options) {
return new L.Control.Sidebar(placeholder, options);
};

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

5733
gui-src/js/maps/proj4-src.js Normal file
View File

File diff suppressed because it is too large Load Diff

3
gui-src/js/maps/proj4.js Normal file
View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,227 @@
(function (factory) {
var L, proj4;
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet', 'proj4'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet');
proj4 = require('proj4');
module.exports = factory(L, proj4);
} else {
// Browser globals
if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined')
throw "Leaflet and proj4 must be loaded first";
factory(window.L, window.proj4);
}
}(function (L, proj4) {
L.Proj = {};
L.Proj._isProj4Obj = function(a) {
return (typeof a['inverse'] !== 'undefined'
&& typeof a['forward'] !== 'undefined');
};
L.Proj.ScaleDependantTransformation = function(scaleTransforms) {
this.scaleTransforms = scaleTransforms;
}
L.Proj.ScaleDependantTransformation.prototype.transform = function(point, scale) {
return this.scaleTransforms[scale].transform(point, scale);
};
L.Proj.ScaleDependantTransformation.prototype.untransform = function(point, scale) {
return this.scaleTransforms[scale].untransform(point, scale);
};
L.Proj.Projection = L.Class.extend({
initialize: function(a, def) {
if (L.Proj._isProj4Obj(a)) {
this._proj = a;
} else {
var code = a;
if (def) {
proj4.defs(code, def);
} else if (proj4.defs[code] === undefined) {
var urn = code.split(':');
if (urn.length > 3)
code = urn[urn.length - 3] + ':' + urn[urn.length - 1];
if (proj4.defs[code] === undefined)
throw "No projection definition for code " + code;
}
this._proj = proj4(code);
}
},
project: function (latlng) {
var point = this._proj.forward([latlng.lng, latlng.lat]);
return new L.Point(point[0], point[1]);
},
unproject: function (point, unbounded) {
var point2 = this._proj.inverse([point.x, point.y]);
return new L.LatLng(point2[1], point2[0], unbounded);
}
});
L.Proj.CRS = L.Class.extend({
includes: L.CRS,
options: {
transformation: new L.Transformation(1, 0, -1, 0)
},
initialize: function(a, b, c) {
var code, proj, def, options;
if (L.Proj._isProj4Obj(a)) {
proj = a;
code = proj.srsCode;
options = b || {};
this.projection = new L.Proj.Projection(proj);
} else {
code = a;
def = b;
options = c || {};
this.projection = new L.Proj.Projection(code, def);
}
L.Util.setOptions(this, options);
this.code = code;
this.transformation = this.options.transformation;
if (this.options.origin) {
this.transformation =
new L.Transformation(1, -this.options.origin[0],
-1, this.options.origin[1]);
}
if (this.options.scales) {
this.scale = function(zoom) {
return this.options.scales[zoom];
}
} else if (this.options.resolutions) {
this.scale = function(zoom) {
return 1 / this.options.resolutions[zoom];
}
}
}
});
L.Proj.CRS.TMS = L.Proj.CRS.extend({
initialize: function(a, b, c, d) {
if (L.Proj._isProj4Obj(a)) {
var proj = a,
projectedBounds = b,
options = c || {};
options.origin = [projectedBounds[0], projectedBounds[3]];
L.Proj.CRS.prototype.initialize.call(this, proj, options);
} else {
var code = a,
def = b,
projectedBounds = c,
options = d || {};
options.origin = [projectedBounds[0], projectedBounds[3]];
L.Proj.CRS.prototype.initialize.call(this, code, def, options);
}
this.projectedBounds = projectedBounds;
}
});
L.Proj.TileLayer = {};
L.Proj.TileLayer.TMS = L.TileLayer.extend({
options: {
tms: true,
continuousWorld: true
},
initialize: function(urlTemplate, crs, options) {
var boundsMatchesGrid = true,
scaleTransforms,
upperY,
crsBounds;
if (!(crs instanceof L.Proj.CRS.TMS)) {
throw new Error("CRS is not L.Proj.CRS.TMS.");
}
L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
this.crs = crs;
crsBounds = this.crs.projectedBounds;
// Verify grid alignment
for (var i = this.options.minZoom; i < this.options.maxZoom && boundsMatchesGrid; i++) {
var gridHeight = (crsBounds[3] - crsBounds[1]) /
this._projectedTileSize(i);
boundsMatchesGrid = Math.abs(gridHeight - Math.round(gridHeight)) > 1e-3;
}
if (!boundsMatchesGrid) {
scaleTransforms = {};
for (var i = this.options.minZoom; i < this.options.maxZoom; i++) {
upperY = crsBounds[1] + Math.ceil((crsBounds[3] - crsBounds[1]) /
this._projectedTileSize(i)) * this._projectedTileSize(i);
scaleTransforms[this.crs.scale(i)] = new L.Transformation(1, -crsBounds[0], -1, upperY);
}
this.crs = new L.Proj.CRS.TMS(this.crs.projection._proj, crsBounds, this.crs.options);
this.crs.transformation = new L.Proj.ScaleDependantTransformation(scaleTransforms);
}
},
getTileUrl: function(tilePoint, zoom) {
var gridHeight = Math.ceil(
(this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) /
this._projectedTileSize(zoom)
);
return L.Util.template(this._url, L.Util.extend({
s: this._getSubdomain(tilePoint),
z: zoom,
x: tilePoint.x,
y: gridHeight - tilePoint.y - 1
}, this.options));
},
_projectedTileSize: function(zoom) {
return (this.options.tileSize / this.crs.scale(zoom));
}
});
L.Proj.GeoJSON = L.GeoJSON.extend({
initialize: function(geojson, options) {
if (geojson.crs && geojson.crs.type == 'name') {
var crs = new L.Proj.CRS(geojson.crs.properties.name)
options = options || {};
options.coordsToLatLng = function(coords) {
var point = L.point(coords[0], coords[1]);
return crs.projection.unproject(point);
};
}
L.GeoJSON.prototype.initialize.call(this, geojson, options);
}
});
L.Proj.geoJson = function(geojson, options) {
return new L.Proj.GeoJSON(geojson, options);
};
if (typeof L.CRS !== 'undefined') {
// This is left here for backwards compatibility
L.CRS.proj4js = (function () {
return function (code, def, transformation, options) {
options = options || {};
if (transformation) options.transformation = transformation;
return new L.Proj.CRS(code, def, options);
};
}());
}
return L.Proj;
}));

View File

@@ -0,0 +1,158 @@
(function( definition ) { // execute immeidately
if ( typeof module !== 'undefined' &&
typeof module.exports !== 'undefined' ) {
module.exports = definition();
}
else if ( typeof window === "object" ) {
// example run syntax: BBOX_T( { 'url' : '/js/maps/testdata.js' } );
window.BBOX_T = definition();
}
})( function() {
'use strict';
/*
**
** constructor
**
*/
var TestRunner = function( options ) {
options || ( options = {} );
if( !this || !(this instanceof TestRunner )){
return new TestRunner( options );
}
this.test_url = options.url || "";
this.global_setup(); // execute immediately
};
/*
**
** functions
**
*/
TestRunner.prototype.global_setup = function() {
var self = this; // hold ref to instance
$.ajax({
'url' : this.test_url ,
'dataType' : 'json'
})
.done( function( json_data ) {
self.run_this_mother.call( self, json_data );
})
.fail( function( error ) {
console.log( "The test data didn't load: ", error );
});
};
TestRunner.prototype.single_setup = function() {
this.get_layer_count();
};
TestRunner.prototype.tear_down = function() {
if( this._draw_delete_handler ){
this._draw_delete_handler.off('draw:deleted');
}
};
TestRunner.prototype.run_this_mother = function( json_data ) {
for( var key in json_data ){
console.log( "[ RUNNING ]: test " + json_data[key]['type'] + "->" + "simple=" + json_data[key]['simple'] );
var data = json_data[key]['data'];
if( json_data[key]['type'] === 'geojson' ) {
data = JSON.stringify( data );
}
/*
** run different tests
** the context here is jQuery, so change
** to reference the instance
*/
this.single_setup();
this.test_parsing( data, json_data );
this.test_add2map( json_data );
this.test_deletable( json_data );
this.tear_down();
}
};
TestRunner.prototype.test_deletable = function(identifier){ // TODO: this needs work
var toolbar = null;
// get the right toolbar, depending on attributes
for( var key in drawControl._toolbars ){
var tbar = drawControl._toolbars[key];
if ( !(tbar instanceof L.EditToolbar ) ){
continue;
}
toolbar = tbar; // set the right one;
}
// create delete handler that makes sure things are deleted
this._draw_delete_handler = map.on('draw:deleted', function (e) {
try {
e.layers.eachLayer(function (l) {
drawnItems.removeLayer(l);
});
console.warn( "[ PASSED ]: test_deletable" );
}
catch ( err ) {
console.error( "[ DELETE TEST FAIL ]: ", err.message, identifier );
}
});
// loop through this toolbars featureGroup, delete layers
if ( !toolbar._activeMode ) {
toolbar._modes['remove'].button.click(); // enable deletable
}
for( var indx in toolbar.options['featureGroup']._layers ) {
try {
var lyr = toolbar.options['featureGroup']._layers[indx];
lyr.fire( 'click' ); // triggers delete
}
catch ( err ){
console.error( "[ DELETE TEST FAIL ]: ", err.message, identifier );
}
}
// WTF?
$('a[title="Save changes."]')[0].click(); // disable deletable
};
TestRunner.prototype.test_add2map = function(identifier){
var current_num = Object.keys( map._layers ).length;
if( current_num <= this.num_layers_before_parse ){
console.error( "[ ADD2MAP TEST FAIL ]: ", identifier );
}
else {
console.warn( "[ PASSED ]: test_add2map" );
}
};
TestRunner.prototype.get_layer_count = function(){
this.num_layers_before_parse = Object.keys( map._layers ).length;
};
TestRunner.prototype.test_parsing = function( data, identifier ){
var is_valid = FormatSniffer( { data : data } ).sniff();
if ( !is_valid ) {
console.error( "[ PARSE TEST FAIL ]: ", identifier );
}
else {
console.warn( "[ PASSED ]: test_parsing" );
}
};
return TestRunner; // return class def
});

View File

@@ -0,0 +1,549 @@
/*
**
** taking only necessary pieces from
** https://raw.github.com/arthur-e/Wicket/master/wicket.src.js
**
*/
var Wkt = (function () { // Execute function immediately
var beginsWith, endsWith;
/**
* Returns true if the substring is found at the beginning of the string.
* @param str {String} The String to search
* @param sub {String} The substring of interest
* @return {Boolean}
* @private
*/
beginsWith = function (str, sub) {
return str.substring(0, sub.length) === sub;
};
/**
* Returns true if the substring is found at the end of the string.
* @param str {String} The String to search
* @param sub {String} The substring of interest
* @return {Boolean}
* @private
*/
endsWith = function (str, sub) {
return str.substring(str.length - sub.length) === sub;
};
return {
/**
* The default delimiter for separating components of atomic geometry (coordinates)
* @ignore
*/
delimiter: ' ',
/**
* Determines whether or not the passed Object is an Array.
* @param obj {Object} The Object in question
* @return {Boolean}
* @member Wkt.isArray
* @method
*/
isArray: function (obj) {
return !!(obj && obj.constructor === Array);
},
/**
* Removes given character String(s) from a String.
* @param str {String} The String to search
* @param sub {String} The String character(s) to trim
* @return {String} The trimmed string
* @member Wkt.trim
* @method
*/
trim: function (str, sub) {
sub = sub || ' '; // Defaults to trimming spaces
// Trim beginning spaces
while (beginsWith(str, sub)) {
str = str.substring(1);
}
// Trim ending spaces
while (endsWith(str, sub)) {
str = str.substring(0, str.length - 1);
}
return str;
},
/**
* An object for reading WKT strings and writing geographic features
* @constructor Wkt.Wkt
* @param initializer {String} An optional WKT string for immediate read
* @property {Array} components - Holder for atomic geometry objects (internal representation of geometric components)
* @property {String} delimiter - The default delimiter for separating components of atomic geometry (coordinates)
* @property {Object} regExes - Some regular expressions copied from OpenLayers.Format.WKT.js
* @property {String} type - The Well-Known Text name (e.g. 'point') of the geometry
* @property {Boolean} wrapVerticies - True to wrap vertices in MULTIPOINT geometries; If true: MULTIPOINT((30 10),(10 30),(40 40)); If false: MULTIPOINT(30 10,10 30,40 40)
* @return {Wkt.Wkt}
* @memberof Wkt
*/
Wkt: function (initializer) {
/**
* The default delimiter between X and Y coordinates.
* @ignore
*/
this.delimiter = Wkt.delimiter;
/**
* Configuration parameter for controlling how Wicket seralizes
* MULTIPOINT strings. Examples; both are valid WKT:
* If true: MULTIPOINT((30 10),(10 30),(40 40))
* If false: MULTIPOINT(30 10,10 30,40 40)
* @ignore
*/
this.wrapVertices = true;
/**
* Some regular expressions copied from OpenLayers.Format.WKT.js
* @ignore
*/
this.regExes = {
'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
'spaces': /\s+|\+/, // Matches the '+' or the empty space
'numeric': /-*\d+(\.*\d+)?/,
'comma': /\s*,\s*/,
'parenComma': /\)\s*,\s*\(/,
'coord': /-*\d+\.*\d+ -*\d+\.*\d+/, // e.g. "24 -14"
'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,
'trimParens': /^\s*\(?(.*?)\)?\s*$/
};
/**
* The internal representation of geometry--the "components" of geometry.
* @ignore
*/
this.components = undefined;
// An initial WKT string may be provided
if (initializer && typeof initializer === 'string') {
this.read(initializer);
} else if (initializer && typeof initializer !== undefined) {
this.fromObject(initializer);
}
}
};
}());
/**
* Returns true if the internal geometry is a collection of geometries.
* @return {Boolean} Returns true when it is a collection
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.isCollection = function () {
switch (this.type.slice(0, 5)) {
case 'multi':
// Trivial; any multi-geometry is a collection
return true;
case 'polyg':
// Polygons with holes are "collections" of rings
return true;
default:
// Any other geometry is not a collection
return false;
}
};
/**
* Compares two x,y coordinates for equality.
* @param a {Object} An object with x and y properties
* @param b {Object} An object with x and y properties
* @return {Boolean}
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.sameCoords = function (a, b) {
return (a.x === b.x && a.y === b.y);
};
/**
* Sets internal geometry (components) from framework geometry (e.g.
* Google Polygon objects or google.maps.Polygon).
* @param obj {Object} The framework-dependent geometry representation
* @return {Wkt.Wkt} The object itself
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.fromObject = function (obj) {
var result = this.deconstruct.call(this, obj);
this.components = result.components;
this.isRectangle = result.isRectangle || false;
this.type = result.type;
return this;
};
/**
* Creates external geometry objects based on a plug-in framework's
* construction methods and available geometry classes.
* @param config {Object} An optional framework-dependent properties specification
* @return {Object} The framework-dependent geometry representation
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.toObject = function (config) {
return this.construct[this.type].call(this, config);
};
/**
* Absorbs the geometry of another Wkt.Wkt instance, merging it with its own,
* creating a collection (MULTI-geometry) based on their types, which must agree.
* For example, creates a MULTIPOLYGON from a POLYGON type merged with another
<<<<<<< HEAD
* POLYGON type.
=======
* POLYGON type, or adds a POLYGON instance to a MULTIPOLYGON instance.
>>>>>>> dev
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.merge = function (wkt) {
var prefix = this.type.slice(0, 5);
if (this.type !== wkt.type) {
if (this.type.slice(5, this.type.length) !== wkt.type) {
throw TypeError('The input geometry types must agree or the calling Wkt.Wkt instance must be a multigeometry of the other');
}
}
switch (prefix) {
case 'point':
this.components = [this.components.concat(wkt.components)];
break;
case 'multi':
this.components = this.components.concat((wkt.type.slice(0, 5) === 'multi') ? wkt.components : [wkt.components]);
break;
default:
this.components = [
this.components,
wkt.components
]
break;
}
if (prefix !== 'multi') {
this.type = 'multi' + this.type;
}
};
/**
* Reads a WKT string, validating and incorporating it.
* @param wkt {String} A WKT string
* @return {Array} An Array of internal geometry objects
* @memberof Wkt.Wkt
* @method
*/
Wkt.Wkt.prototype.read = function (wkt) {
var matches;
matches = this.regExes.typeStr.exec(wkt);
if (matches) {
this.type = matches[1].toLowerCase();
this.base = matches[2];
if (this.ingest[this.type]) {
this.components = this.ingest[this.type].apply(this, [this.base]);
}
} else {
console.log("Invalid WKT string provided to read()");
throw {
name: "WKTError",
message: "Invalid WKT string provided to read()"
};
}
return this.components;
}; // eo readWkt
/**
* This object contains functions as property names that ingest WKT
* strings into the internal representation.
* @memberof Wkt.Wkt
* @namespace Wkt.Wkt.ingest
* @instance
*/
Wkt.Wkt.prototype.ingest = {
/**
* Return point feature given a point WKT fragment.
* @param str {String} A WKT fragment representing the point
* @memberof Wkt.Wkt.ingest
* @instance
*/
point: function (str) {
var coords = Wkt.trim(str).split(this.regExes.spaces);
// In case a parenthetical group of coordinates is passed...
return [{ // ...Search for numeric substrings
x: parseFloat(this.regExes.numeric.exec(coords[0])[0]),
y: parseFloat(this.regExes.numeric.exec(coords[1])[0])
}];
},
/**
* Return a multipoint feature given a multipoint WKT fragment.
* @param str {String} A WKT fragment representing the multipoint
* @memberof Wkt.Wkt.ingest
* @instance
*/
multipoint: function (str) {
var i, components, points;
components = [];
points = Wkt.trim(str).split(this.regExes.comma);
for (i = 0; i < points.length; i += 1) {
components.push(this.ingest.point.apply(this, [points[i]]));
}
return components;
},
/**
* Return a linestring feature given a linestring WKT fragment.
* @param str {String} A WKT fragment representing the linestring
* @memberof Wkt.Wkt.ingest
* @instance
*/
linestring: function (str) {
var i, multipoints, components;
// In our x-and-y representation of components, parsing
// multipoints is the same as parsing linestrings
multipoints = this.ingest.multipoint.apply(this, [str]);
// However, the points need to be joined
components = [];
for (i = 0; i < multipoints.length; i += 1) {
components = components.concat(multipoints[i]);
}
return components;
},
/**
* Return a multilinestring feature given a multilinestring WKT fragment.
* @param str {String} A WKT fragment representing the multilinestring
* @memberof Wkt.Wkt.ingest
* @instance
*/
multilinestring: function (str) {
var i, components, line, lines;
components = [];
lines = Wkt.trim(str).split(this.regExes.doubleParenComma);
if (lines.length === 1) { // If that didn't work...
lines = Wkt.trim(str).split(this.regExes.parenComma);
}
for (i = 0; i < lines.length; i += 1) {
line = lines[i].replace(this.regExes.trimParens, '$1');
components.push(this.ingest.linestring.apply(this, [line]));
}
return components;
},
/**
* Return a polygon feature given a polygon WKT fragment.
* @param str {String} A WKT fragment representing the polygon
* @memberof Wkt.Wkt.ingest
* @instance
*/
polygon: function (str) {
var i, j, components, subcomponents, ring, rings;
rings = Wkt.trim(str).split(this.regExes.parenComma);
components = []; // Holds one or more rings
for (i = 0; i < rings.length; i += 1) {
ring = rings[i].replace(this.regExes.trimParens, '$1').split(this.regExes.comma);
subcomponents = []; // Holds the outer ring and any inner rings (holes)
for (j = 0; j < ring.length; j += 1) {
// Split on the empty space or '+' character (between coordinates)
subcomponents.push({
x: parseFloat(ring[j].split(this.regExes.spaces)[0]),
y: parseFloat(ring[j].split(this.regExes.spaces)[1])
});
}
components.push(subcomponents);
}
return components;
},
/**
* Return a multipolygon feature given a multipolygon WKT fragment.
* @param str {String} A WKT fragment representing the multipolygon
* @memberof Wkt.Wkt.ingest
* @instance
*/
multipolygon: function (str) {
var i, components, polygon, polygons;
components = [];
polygons = Wkt.trim(str).split(this.regExes.doubleParenComma);
for (i = 0; i < polygons.length; i += 1) {
polygon = polygons[i].replace(this.regExes.trimParens, '$1');
components.push(this.ingest.polygon.apply(this, [polygon]));
}
return components;
},
/**
* Return an array of features given a geometrycollection WKT fragment.
* @param str {String} A WKT fragment representing the geometry collection
* @memberof Wkt.Wkt.ingest
* @instance
*/
geometrycollection: function (str) {
console.log('The geometrycollection WKT type is not yet supported.');
}
}; // eo ingest
/**
* @augments Wkt.Wkt
* Truncates an Array of coordinates by the closing coordinate when it is
* equal to the first coordinate given--this is only to be used for closed
* geometries in order to provide merely an "implied" closure to Leaflet.
* @param coords {Array} An Array of x,y coordinates (objects)
* @return {Array}
*/
Wkt.Wkt.prototype.trunc = function (coords) {
var i, verts = [];
for (i = 0; i < coords.length; i += 1) {
if (Wkt.isArray(coords[i])) {
verts.push(this.trunc(coords[i]));
} else {
// Add the first coord, but skip the last if it is identical
if (i === 0 || !this.sameCoords(coords[0], coords[i])) {
verts.push(coords[i]);
}
}
}
return verts;
};
/**
* @augments Wkt.Wkt
* An object of framework-dependent construction methods used to generate
* objects belonging to the various geometry classes of the framework.
*/
Wkt.Wkt.prototype.construct = {
/**
* Creates the framework's equivalent point geometry object.
* @param config {Object} An optional properties hash the object should use
* @param component {Object} An optional component to build from
* @return {L.marker}
*/
point: function (config, component) {
var coord = component || this.components;
if (coord instanceof Array) {
coord = coord[0];
}
return L.marker(this.coordsToLatLng(coord), config);
},
/**
* Creates the framework's equivalent multipoint geometry object.
* @param config {Object} An optional properties hash the object should use
* @return {L.featureGroup}
*/
multipoint: function (config) {
var i,
layers = [],
coords = this.components;
for (i = 0; i < coords.length; i += 1) {
layers.push(this.construct.point.call(this, config, coords[i]));
}
return L.featureGroup(layers, config);
},
/**
* Creates the framework's equivalent linestring geometry object.
* @param config {Object} An optional properties hash the object should use
* @param component {Object} An optional component to build from
* @return {L.polyline}
*/
linestring: function (config, component) {
var coords = component || this.components,
latlngs = this.coordsToLatLngs(coords);
return L.polyline(latlngs, config);
},
/**
* Creates the framework's equivalent multilinestring geometry object.
* @param config {Object} An optional properties hash the object should use
* @return {L.multiPolyline}
*/
multilinestring: function (config) {
var coords = this.components,
latlngs = this.coordsToLatLngs(coords, 1);
return L.multiPolyline(latlngs, config);
},
/**
* Creates the framework's equivalent polygon geometry object.
* @param config {Object} An optional properties hash the object should use
* @return {L.multiPolygon}
*/
polygon: function (config) {
// Truncate the coordinates to remove the closing coordinate
var coords = this.trunc(this.components),
latlngs = this.coordsToLatLngs(coords, 1);
return L.polygon(latlngs, config);
},
/**
* Creates the framework's equivalent multipolygon geometry object.
* @param config {Object} An optional properties hash the object should use
* @return {L.multiPolygon}
*/
multipolygon: function (config) {
// Truncate the coordinates to remove the closing coordinate
var coords = this.trunc(this.components),
latlngs = this.coordsToLatLngs(coords, 2);
return L.multiPolygon(latlngs, config);
},
/**
* Creates the framework's equivalent collection of geometry objects.
* @param config {Object} An optional properties hash the object should use
* @return {L.featureGroup}
*/
geometrycollection: function (config) {
var comps, i, layers;
comps = this.trunc(this.components);
layers = [];
for (i = 0; i < this.components.length; i += 1) {
layers.push(this.construct[comps[i].type].call(this, comps[i]));
}
return L.featureGroup(layers, config);
}
};
L.Util.extend(Wkt.Wkt.prototype, {
coordsToLatLngs: L.GeoJSON.coordsToLatLngs,
// TODO Why doesn't the coordsToLatLng function in L.GeoJSON already suffice?
coordsToLatLng: function (coords, reverse) {
var lat = reverse ? coords.x : coords.y,
lng = reverse ? coords.y : coords.x;
return L.latLng(lat, lng, true);
}
});

60
gui-src/maps.html Normal file
View File

@@ -0,0 +1,60 @@
<html>
<head>
<title>Map View</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<link rel="stylesheet" href="css/maps/leaflet.css" />
<link rel="stylesheet" href="css/maps/leaflet.draw.css" />
<link rel="stylesheet" href="css/maps/leaflet.sidebar.css" />
<link rel="stylesheet" href="css/maps/mapbox.v3.2.0.css" />
<link rel="stylesheet" href="css/bbox.css" />
<script src="js/libs/jquery-1.9.1.min.js"></script>
<script src="js/libs/jquery-ui-1.10.3.custom.js"></script>
<script src="js/maps/leaflet.js"></script>
<script src="js/maps/mapbox.v3.2.0.js"></script>
<script src="js/maps/leaflet.draw.js"></script>
<script src="js/maps/leaflet.sidebar.js"></script>
<script src="js/maps/wkt.parser.js"></script>
<script src="js/maps/proj4-src.js"></script>
<script src="js/maps/proj4leaflet.js"></script>
<script src="js/bbox.js"></script>
</head>
<body>
<div id="map"></div>
<div id="rsidebar" >
<button id="add">Add</button>
<button id="clear">Clear</button>
</div>
<div id="lsidebar">
</div>
<section id="info-box">
<div id="info">
<div id="wgscoords" class="coords">
<div id="bbounds">
<span id="bbboundslabel" class="bboxlabel">BBOX</span>
<span id="boxbounds" class="bboxllpos"></span>
</div>
</div>
<div id="projcoords" class="coords">
<div id="bbounds">
<span id="bbboundslabel" class="bboxlabel">BBOX</span>
<span id="boxboundsmerc" class="bboxprojpos"></span>
</div>
</div>
</div>
</section>
<div id="ocontainer" class="ocontainer" style="display:none;">
<textarea rows="25" cols="100" ></textarea>
<div id="submit-buttons">
<button id="cancel">Cancel</button>
<button id="add">Add</button>
</div>
</div>
</body>
</html>

BIN
icons/128x128.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
icons/128x128@2x.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
icons/32x32.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
icons/icon.icns Normal file
View File

Binary file not shown.

BIN
icons/icon.ico Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

BIN
icons/icon.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

View File

@@ -145,31 +145,31 @@ impl Block {
pub fn properties(&self) -> Option<Value> {
match self.id {
105 => Some(Value::Compound({
let mut map = HashMap::new();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("age".to_string(), Value::String("7".to_string()));
map
})),
106 => Some(Value::Compound({
let mut map = HashMap::new();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("half".to_string(), Value::String("lower".to_string()));
map
})),
107 => Some(Value::Compound({
let mut map = HashMap::new();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("half".to_string(), Value::String("upper".to_string()));
map
})),
108 => Some(Value::Compound({
let mut map = HashMap::new();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("age".to_string(), Value::String("7".to_string()));
map
})),
109 => Some(Value::Compound({
let mut map = HashMap::new();
let mut map: HashMap<String, Value> = HashMap::new();
map.insert("age".to_string(), Value::String("7".to_string()));
map
})),

View File

@@ -19,29 +19,29 @@ pub fn color_text_to_rgb_tuple(text: &str) -> Option<RGBTuple> {
fn full_hex_color_to_rgb_tuple(text: &str) -> Option<RGBTuple> {
if text.len() != 7
|| !text.starts_with("#")
|| !text.chars().skip(1).all(|c| c.is_ascii_hexdigit())
|| !text.chars().skip(1).all(|c: char| c.is_ascii_hexdigit())
{
return None;
}
let r = u8::from_str_radix(&text[1..3], 16).unwrap();
let g = u8::from_str_radix(&text[3..5], 16).unwrap();
let b = u8::from_str_radix(&text[5..7], 16).unwrap();
let r: u8 = u8::from_str_radix(&text[1..3], 16).unwrap();
let g: u8 = u8::from_str_radix(&text[3..5], 16).unwrap();
let b: u8 = u8::from_str_radix(&text[5..7], 16).unwrap();
Some((r, g, b))
}
fn short_hex_color_to_rgb_tuple(text: &str) -> Option<RGBTuple> {
if text.len() != 4
|| !text.starts_with("#")
|| text.chars().skip(1).all(|c| c.is_ascii_hexdigit())
|| text.chars().skip(1).all(|c: char| c.is_ascii_hexdigit())
{
return None;
}
let r = u8::from_str_radix(&text[1..2], 16).unwrap();
let r = r | r << 4;
let g = u8::from_str_radix(&text[2..3], 16).unwrap();
let g = g | g << 4;
let b = u8::from_str_radix(&text[3..4], 16).unwrap();
let b = b | b << 4;
let r: u8 = u8::from_str_radix(&text[1..2], 16).unwrap();
let r: u8 = r | r << 4;
let g: u8 = u8::from_str_radix(&text[2..3], 16).unwrap();
let g: u8 = g | g << 4;
let b: u8 = u8::from_str_radix(&text[3..4], 16).unwrap();
let b: u8 = b | b << 4;
Some((r, g, b))
}
@@ -78,11 +78,11 @@ fn color_name_to_rgb_tuple(text: &str) -> Option<RGBTuple> {
pub fn rgb_distance(from: &RGBTuple, to: &RGBTuple) -> u32 {
// i32 because .pow(2) returns the same data type as self and 255^2 wouldn't fit
let difference = (
let difference: (i32, i32, i32) = (
from.0 as i32 - to.0 as i32,
from.1 as i32 - to.1 as i32,
from.2 as i32 - to.2 as i32,
);
let distance = difference.0.pow(2) + difference.1.pow(2) + difference.2.pow(2);
let distance: i32 = difference.0.pow(2) + difference.1.pow(2) + difference.2.pow(2);
distance as u32
}

View File

@@ -1,37 +1,45 @@
use crate::args::Args;
use crate::block_definitions::{BEDROCK, DIRT, GRASS_BLOCK, STONE};
use crate::block_definitions::{DIRT, GRASS_BLOCK};
use crate::element_processing::*;
use crate::osm_parser::ProcessedElement;
use crate::progress::emit_gui_progress_update;
use crate::world_editor::WorldEditor;
use colored::Colorize;
use indicatif::{ProgressBar, ProgressStyle};
const MIN_Y: i32 = -64;
const MAX_Y: i32 = 256;
const GROUND_LEVEL: i32 = 65;
const GROUND_LEVEL: i32 = -62;
pub fn generate_world(
elements: Vec<ProcessedElement>,
args: &Args,
scale_factor_x: f64,
scale_factor_z: f64,
) {
) -> Result<(), String> {
println!("{} Processing data...", "[3/5]".bold());
emit_gui_progress_update(10.0, "Processing data...");
let region_dir: String = format!("{}/region", args.path);
let mut editor: WorldEditor =
WorldEditor::new(&region_dir, scale_factor_x, scale_factor_z, args);
// Process data
let process_pb: ProgressBar = ProgressBar::new(elements.len() as u64);
let elements_count: usize = elements.len();
let process_pb: ProgressBar = ProgressBar::new(elements_count as u64);
process_pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:45.white/black}] {pos}/{len} elements ({eta}) {msg}")
.unwrap()
.progress_chars("█▓░"));
let progress_increment_prcs: f64 = 50.0 / elements_count as f64;
let mut current_progress_prcs: f64 = 10.0;
let mut last_emitted_progress: f64 = current_progress_prcs;
for element in &elements {
process_pb.inc(1);
current_progress_prcs += progress_increment_prcs;
if (current_progress_prcs - last_emitted_progress).abs() > 0.25 {
emit_gui_progress_update(current_progress_prcs, "");
last_emitted_progress = current_progress_prcs;
}
if args.debug {
process_pb.set_message(format!(
@@ -149,6 +157,8 @@ pub fn generate_world(
let mut block_counter: u64 = 0;
println!("{} Generating ground layer...", "[4/5]".bold());
emit_gui_progress_update(60.0, "Generating ground layer...");
let ground_pb: ProgressBar = ProgressBar::new(total_blocks);
ground_pb.set_style(
ProgressStyle::default_bar()
@@ -157,34 +167,26 @@ pub fn generate_world(
.progress_chars("█▓░"),
);
let mut gui_progress_grnd: f64 = 60.0;
let mut last_emitted_progress: f64 = gui_progress_grnd;
let total_iterations_grnd: f64 = (scale_factor_x + 1.0) * (scale_factor_z + 1.0);
let progress_increment_grnd: f64 = 30.0 / total_iterations_grnd;
for x in 0..=(scale_factor_x as i32) {
for z in 0..=(scale_factor_z as i32) {
// Use the smaller of [current block y, ground level y]
let max_y = (MIN_Y..MAX_Y)
.find(|y| editor.block_at(x, *y, z))
.unwrap_or(MAX_Y)
.min(GROUND_LEVEL);
// 1 layer of grass
editor.set_block(GRASS_BLOCK, x, max_y, z, None, None);
// 3 layers of dirt
for y in (max_y - 3)..max_y {
editor.set_block(DIRT, x, y, z, None, None);
}
// n - 1 layers of stone
for y in (MIN_Y + 1)..(max_y - 3) {
editor.set_block(STONE, x, y, z, None, None);
}
// 1 layer of bedrock
editor.set_block(BEDROCK, x, MIN_Y, z, None, None);
editor.set_block(GRASS_BLOCK, x, GROUND_LEVEL, z, None, None);
editor.set_block(DIRT, x, GROUND_LEVEL - 1, z, None, None);
block_counter += 1;
if block_counter % batch_size == 0 {
ground_pb.inc(batch_size);
}
gui_progress_grnd += progress_increment_grnd;
if (gui_progress_grnd - last_emitted_progress).abs() > 0.25 {
emit_gui_progress_update(gui_progress_grnd, "");
last_emitted_progress = gui_progress_grnd;
}
}
}
@@ -194,5 +196,7 @@ pub fn generate_world(
// Save world
editor.save();
println!("{}", "Done! World generation complete.".green().bold());
emit_gui_progress_update(100.0, "Done! World generation completed.");
println!("{}", "Done! World generation completed.".green().bold());
Ok(())
}

View File

@@ -26,7 +26,10 @@ pub fn generate_amenities(
}
if let Some(amenity_type) = element.tags().get("amenity") {
let first_node = element.nodes().map(|n| (n.x, n.z)).next();
let first_node: Option<(i32, i32)> = element
.nodes()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.next();
match amenity_type.as_str() {
"waste_disposal" | "waste_basket" => {
// Place a cauldron for waste disposal or waste basket
@@ -41,10 +44,13 @@ pub fn generate_amenities(
}
}
"bicycle_parking" => {
let ground_block = OAK_PLANKS;
let roof_block = STONE_BLOCK_SLAB;
let ground_block: Block = OAK_PLANKS;
let roof_block: Block = STONE_BLOCK_SLAB;
let polygon_coords: Vec<(i32, i32)> = element.nodes().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let floor_area: Vec<(i32, i32)> =
flood_fill_area(&polygon_coords, floodfill_timeout);
@@ -55,8 +61,8 @@ pub fn generate_amenities(
// Place fences and roof slabs at each corner node directly
for node in element.nodes() {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
for y in 1..=4 {
editor.set_block(ground_block, x, ground_level, z, None, None);
@@ -98,8 +104,8 @@ pub fn generate_amenities(
_ => GRAY_CONCRETE,
};
for node in element.nodes() {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
if let Some(prev) = previous_node {
// Create borders for fountain or parking area

View File

@@ -28,13 +28,13 @@ pub fn generate_barriers(editor: &mut WorldEditor, element: &ProcessedElement, g
// Process nodes to create the barrier wall
for i in 1..way.nodes.len() {
let prev = &way.nodes[i - 1];
let x1 = prev.x;
let z1 = prev.z;
let prev: &crate::osm_parser::ProcessedNode = &way.nodes[i - 1];
let x1: i32 = prev.x;
let z1: i32 = prev.z;
let cur = &way.nodes[i];
let x2 = cur.x;
let z2 = cur.z;
let cur: &crate::osm_parser::ProcessedNode = &way.nodes[i];
let x2: i32 = cur.x;
let z2: i32 = cur.z;
// Generate the line of coordinates between the two nodes
let bresenham_points: Vec<(i32, i32, i32)> =

View File

@@ -15,27 +15,27 @@ pub fn generate_bridges(editor: &mut WorldEditor, element: &ProcessedWay, ground
let total_steps: usize = element
.nodes
.windows(2)
.map(|nodes| {
let x1 = nodes[0].x;
let z1 = nodes[0].z;
let x2 = nodes[1].x;
let z2 = nodes[1].z;
.map(|nodes: &[crate::osm_parser::ProcessedNode]| {
let x1: i32 = nodes[0].x;
let z1: i32 = nodes[0].z;
let x2: i32 = nodes[1].x;
let z2: i32 = nodes[1].z;
bresenham_line(x1, ground_level, z1, x2, ground_level, z2).len()
})
.sum();
let half_steps = total_steps / 2; // Calculate midpoint for descending after rising
let mut current_step = 0;
let half_steps: usize = total_steps / 2; // Calculate midpoint for descending after rising
let mut current_step: usize = 0;
for i in 1..element.nodes.len() {
let prev = &element.nodes[i - 1];
let x1 = prev.x;
let z1 = prev.z;
let prev: &crate::osm_parser::ProcessedNode = &element.nodes[i - 1];
let x1: i32 = prev.x;
let z1: i32 = prev.z;
let cur = &element.nodes[i];
let x2 = cur.x;
let z2 = cur.z;
let cur: &crate::osm_parser::ProcessedNode = &element.nodes[i];
let x2: i32 = cur.x;
let z2: i32 = cur.z;
// Generate the line of coordinates between the two nodes
let bresenham_points: Vec<(i32, i32, i32)> =

View File

@@ -24,26 +24,28 @@ pub fn generate_buildings(
let variation_index_wall: usize = rng.gen_range(0..building_wall_variations().len());
let variation_index_floor: usize = rng.gen_range(0..building_floor_variations().len());
let corner_block = building_corner_variations()[variation_index_corner];
let wall_block = element
let corner_block: Block = building_corner_variations()[variation_index_corner];
let wall_block: Block = element
.tags
.get("building:colour")
.and_then(|building_colour| {
color_text_to_rgb_tuple(building_colour)
.map(|rgb| find_nearest_block_in_color_map(&rgb, building_wall_color_map()))
.and_then(|building_colour: &String| {
color_text_to_rgb_tuple(building_colour).map(|rgb: (u8, u8, u8)| {
find_nearest_block_in_color_map(&rgb, building_wall_color_map())
})
})
.flatten()
.unwrap_or_else(|| building_wall_variations()[variation_index_wall]);
let floor_block = element
let floor_block: Block = element
.tags
.get("roof:colour")
.and_then(|roof_colour| {
color_text_to_rgb_tuple(roof_colour)
.map(|rgb| find_nearest_block_in_color_map(&rgb, building_floor_color_map()))
.and_then(|roof_colour: &String| {
color_text_to_rgb_tuple(roof_colour).map(|rgb: (u8, u8, u8)| {
find_nearest_block_in_color_map(&rgb, building_floor_color_map())
})
})
.flatten()
.unwrap_or_else(|| building_floor_variations()[variation_index_floor]);
let window_block = WHITE_STAINED_GLASS;
let window_block: Block = WHITE_STAINED_GLASS;
// Set to store processed flood fill points
let mut processed_points: HashSet<(i32, i32)> = HashSet::new();
@@ -84,11 +86,14 @@ pub fn generate_buildings(
building_height = 2;
if element.tags.contains_key("bicycle_parking") {
let ground_block = OAK_PLANKS;
let roof_block = STONE_BLOCK_SLAB;
let ground_block: Block = OAK_PLANKS;
let roof_block: Block = STONE_BLOCK_SLAB;
let polygon_coords: Vec<(i32, i32)> =
element.nodes.iter().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let floor_area: Vec<(i32, i32)> =
flood_fill_area(&polygon_coords, floodfill_timeout);
@@ -99,8 +104,8 @@ pub fn generate_buildings(
// Place fences and roof slabs at each corner node directly
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
for y in 1..=4 {
editor.set_block(ground_block, x, ground_level, z, None, None);
@@ -118,12 +123,12 @@ pub fn generate_buildings(
return;
}
} else if building_type == "roof" {
let roof_height = ground_level + 5;
let roof_height: i32 = ground_level + 5;
// Iterate through the nodes to create the roof edges using Bresenham's line algorithm
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
if let Some(prev) = previous_node {
let bresenham_points: Vec<(i32, i32, i32)> =
@@ -142,8 +147,11 @@ pub fn generate_buildings(
}
// Use flood-fill to fill the interior of the roof
let polygon_coords: Vec<(i32, i32)> =
element.nodes.iter().map(|node| (node.x, node.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes
.iter()
.map(|node: &crate::osm_parser::ProcessedNode| (node.x, node.z))
.collect();
let roof_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout); // Use flood-fill to determine the area
// Fill the interior of the roof with STONE_BRICK_SLAB
@@ -171,8 +179,8 @@ pub fn generate_buildings(
// Process nodes to create walls and corners
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
if let Some(prev) = previous_node {
// Calculate walls and corners using Bresenham line
@@ -211,7 +219,11 @@ pub fn generate_buildings(
// Flood-fill interior with floor variation
if corner_addup != (0, 0, 0) {
let polygon_coords: Vec<(i32, i32)> = element.nodes.iter().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let floor_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
for (x, z) in floor_area {
@@ -264,21 +276,21 @@ fn generate_bridge(
floodfill_timeout: Option<&Duration>,
) {
// Calculate the bridge level
let mut bridge_level = base_level;
let mut bridge_level: i32 = base_level;
if let Some(level_str) = element.tags.get("level") {
if let Ok(level) = level_str.parse::<i32>() {
bridge_level += (level * 3) + 1; // Adjust height by levels
}
}
let floor_block = STONE;
let railing_block = STONE_BRICKS;
let floor_block: Block = STONE;
let railing_block: Block = STONE_BRICKS;
// Process the nodes to create bridge pathways and railings
let mut previous_node: Option<(i32, i32)> = None;
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
// Create bridge path using Bresenham's line
if let Some(prev) = previous_node {
@@ -293,7 +305,11 @@ fn generate_bridge(
}
// Flood fill the area between the bridge path nodes
let polygon_coords: Vec<(i32, i32)> = element.nodes.iter().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let bridge_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
for (x, z) in bridge_area {
editor.set_block(floor_block, x, bridge_level, z, None, None);

View File

@@ -14,8 +14,8 @@ pub fn generate_doors(editor: &mut WorldEditor, element: &ProcessedNode, ground_
}
}
let x = element.x;
let z = element.z;
let x: i32 = element.x;
let z: i32 = element.z;
// Set the ground block and the door blocks
editor.set_block(GRAY_CONCRETE, x, ground_level, z, None, None);

View File

@@ -16,8 +16,8 @@ pub fn generate_highways(
if highway_type == "street_lamp" {
// Handle street lamps
if let ProcessedElement::Node(first_node) = element {
let x = first_node.x;
let z = first_node.z;
let x: i32 = first_node.x;
let z: i32 = first_node.z;
for y in 1..=4 {
editor.set_block(OAK_FENCE, x, ground_level + y, z, None, None);
}
@@ -28,8 +28,8 @@ pub fn generate_highways(
if let Some(crossing_type) = element.tags().get("crossing") {
if crossing_type == "traffic_signals" {
if let ProcessedElement::Node(node) = element {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
for y in 1..=3 {
editor.set_block(COBBLESTONE_WALL, x, ground_level + y, z, None, None);
}
@@ -43,8 +43,8 @@ pub fn generate_highways(
} else if highway_type == "bus_stop" {
// Handle bus stops
if let ProcessedElement::Node(node) = element {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
for y in 1..=3 {
editor.set_block(COBBLESTONE_WALL, x, ground_level + y, z, None, None);
}
@@ -52,13 +52,17 @@ pub fn generate_highways(
editor.set_block(WHITE_WOOL, x, ground_level + 4, z, None, None);
editor.set_block(WHITE_WOOL, x + 1, ground_level + 4, z, None, None);
}
} else if element.tags().get("area").map_or(false, |v| v == "yes") {
} else if element
.tags()
.get("area")
.map_or(false, |v: &String| v == "yes")
{
let ProcessedElement::Way(way) = element else {
return;
};
// Handle areas like pedestrian plazas
let mut surface_block = STONE; // Default block
let mut surface_block: Block = STONE; // Default block
// Determine the block type based on the 'surface' tag
if let Some(surface) = element.tags().get("surface") {
@@ -77,8 +81,12 @@ pub fn generate_highways(
}
// Fill the area using flood fill or by iterating through the nodes
let polygon_coords: Vec<(i32, i32)> = way.nodes.iter().map(|n| (n.x, n.z)).collect();
let filled_area = flood_fill_area(&polygon_coords, floodfill_timeout);
let polygon_coords: Vec<(i32, i32)> = way
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let filled_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
for (x, z) in filled_area {
editor.set_block(surface_block, x, ground_level, z, None, None);
@@ -144,17 +152,17 @@ pub fn generate_highways(
for node in &way.nodes {
if let Some(prev) = previous_node {
let (x1, z1) = prev;
let x2 = node.x;
let z2 = node.z;
let x2: i32 = node.x;
let z2: i32 = node.z;
// Generate the line of coordinates between the two nodes
let bresenham_points: Vec<(i32, i32, i32)> =
bresenham_line(x1, ground_level, z1, x2, ground_level, z2);
// Variables to manage dashed line pattern
let mut stripe_length = 0;
let dash_length = 5; // Length of the solid part of the stripe
let gap_length = 5; // Length of the gap part of the stripe
let mut stripe_length: i32 = 0;
let dash_length: i32 = 5; // Length of the solid part of the stripe
let gap_length: i32 = 5; // Length of the gap part of the stripe
for (x, _, z) in bresenham_points {
// Draw the road surface for the entire width
@@ -253,11 +261,11 @@ pub fn generate_highways(
/// Generates a siding using stone brick slabs
pub fn generate_siding(editor: &mut WorldEditor, element: &ProcessedWay, ground_level: i32) {
let mut previous_node: Option<(i32, i32)> = None;
let siding_block = STONE_BRICK_SLAB;
let siding_block: Block = STONE_BRICK_SLAB;
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
// Draw the siding using Bresenham's line algorithm between nodes
if let Some(prev) = previous_node {

View File

@@ -41,8 +41,8 @@ pub fn generate_landuse(
// Process landuse nodes to fill the area
for node in &element.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
if let Some(prev) = previous_node {
// Generate the line of coordinates between the two nodes
@@ -166,7 +166,7 @@ pub fn generate_landuse(
if random_choice == 20 {
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
} else if random_choice == 2 {
let flower_block = match rng.gen_range(1..=4) {
let flower_block: Block = match rng.gen_range(1..=4) {
1 => RED_FLOWER,
2 => BLUE_FLOWER,
3 => YELLOW_FLOWER,
@@ -198,7 +198,7 @@ pub fn generate_landuse(
// If a random condition is met, place a special object
if rng.gen_range(0..76) == 0 {
let special_choice = rng.gen_range(1..=10);
let special_choice: i32 = rng.gen_range(1..=10);
if special_choice <= 2 {
create_tree(
editor,
@@ -271,7 +271,7 @@ pub fn generate_landuse(
);
}
} else if random_choice < 30 {
let construction_items = [
let construction_items: [Block; 11] = [
OAK_LOG,
COBBLESTONE,
GRAVEL,

View File

@@ -20,7 +20,7 @@ pub fn generate_leisure(
let mut current_leisure: Vec<(i32, i32)> = vec![];
// Determine block type based on leisure type
let block_type = match leisure_type.as_str() {
let block_type: Block = match leisure_type.as_str() {
"park" => GRASS_BLOCK,
"playground" | "recreation_ground" | "pitch" => {
if let Some(surface) = element.tags.get("surface") {
@@ -73,8 +73,11 @@ pub fn generate_leisure(
// Flood-fill the interior of the leisure area
if corner_addup != (0, 0, 0) {
let polygon_coords: Vec<(i32, i32)> =
element.nodes.iter().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = element
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let filled_area: Vec<(i32, i32)> = flood_fill_area(&polygon_coords, floodfill_timeout);
for (x, z) in filled_area {

View File

@@ -17,8 +17,8 @@ pub fn generate_natural(
if let Some(natural_type) = element.tags().get("natural") {
if natural_type == "tree" {
if let ProcessedElement::Node(node) = element {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
let mut rng: rand::prelude::ThreadRng = rand::thread_rng();
create_tree(editor, x, ground_level + 1, z, rng.gen_range(1..=3));
@@ -29,7 +29,7 @@ pub fn generate_natural(
let mut current_natural: Vec<(i32, i32)> = vec![];
// Determine block type based on natural tag
let block_type = match natural_type.as_str() {
let block_type: Block = match natural_type.as_str() {
"scrub" | "grassland" | "wood" => GRASS_BLOCK,
"beach" | "sand" => SAND,
"tree_row" => GRASS_BLOCK,
@@ -43,8 +43,8 @@ pub fn generate_natural(
// Process natural nodes to fill the area
for node in &way.nodes {
let x = node.x;
let z = node.z;
let x: i32 = node.x;
let z: i32 = node.z;
if let Some(prev) = previous_node {
// Generate the line of coordinates between the two nodes
@@ -63,8 +63,11 @@ pub fn generate_natural(
// If there are natural nodes, flood-fill the area
if corner_addup != (0, 0, 0) {
let polygon_coords: Vec<(i32, i32)> =
way.nodes.iter().map(|n| (n.x, n.z)).collect();
let polygon_coords: Vec<(i32, i32)> = way
.nodes
.iter()
.map(|n: &crate::osm_parser::ProcessedNode| (n.x, n.z))
.collect();
let filled_area: Vec<(i32, i32)> =
flood_fill_area(&polygon_coords, floodfill_timeout);

View File

@@ -22,13 +22,13 @@ pub fn generate_railways(editor: &mut WorldEditor, element: &ProcessedWay, groun
}
for i in 1..element.nodes.len() {
let prev = &element.nodes[i - 1];
let x1 = prev.x;
let z1 = prev.z;
let prev: &crate::osm_parser::ProcessedNode = &element.nodes[i - 1];
let x1: i32 = prev.x;
let z1: i32 = prev.z;
let cur = &element.nodes[i];
let x2 = cur.x;
let z2 = cur.z;
let cur: &crate::osm_parser::ProcessedNode = &element.nodes[i];
let x2: i32 = cur.x;
let z2: i32 = cur.z;
// Generate the line of coordinates between the two nodes
let bresenham_points: Vec<(i32, i32, i32)> =

View File

@@ -17,11 +17,11 @@ pub fn generate_tourisms(editor: &mut WorldEditor, element: &ProcessedNode, grou
}
if let Some(tourism_type) = element.tags.get("tourism") {
let x = element.x;
let z = element.z;
let x: i32 = element.x;
let z: i32 = element.z;
if tourism_type == "information" {
if let Some("board") = element.tags.get("information").map(|x| x.as_str()) {
if let Some("board") = element.tags.get("information").map(|x: &String| x.as_str()) {
// TODO draw a sign
editor.set_block(OAK_PLANKS, x, ground_level + 1, z, None, None);
}

View File

@@ -47,7 +47,7 @@ fn round3(editor: &mut WorldEditor, material: Block, x: i32, y: i32, z: i32) {
/// Function to create different types of trees.
pub fn create_tree(editor: &mut WorldEditor, x: i32, y: i32, z: i32, typetree: u8) {
let mut blacklist = Vec::new();
let mut blacklist: Vec<Block> = Vec::new();
blacklist.extend(building_corner_variations());
blacklist.extend(building_wall_variations());
blacklist.extend(building_floor_variations());

View File

@@ -22,8 +22,8 @@ pub fn generate_water_areas(
}
}
let mut outers = vec![];
let mut inners = vec![];
let mut outers: Vec<Vec<ProcessedNode>> = vec![];
let mut inners: Vec<Vec<ProcessedNode>> = vec![];
for mem in &element.members {
match mem.role {
@@ -43,13 +43,21 @@ pub fn generate_water_areas(
}
let (max_x, max_z) = editor.get_max_coords();
let outers = outers
let outers: Vec<Vec<(f64, f64)>> = outers
.iter()
.map(|x| x.iter().map(|y| (y.x as f64, y.z as f64)).collect())
.map(|x: &Vec<ProcessedNode>| {
x.iter()
.map(|y: &ProcessedNode| (y.x as f64, y.z as f64))
.collect()
})
.collect();
let inners = inners
let inners: Vec<Vec<(f64, f64)>> = inners
.iter()
.map(|x| x.iter().map(|y| (y.x as f64, y.z as f64)).collect())
.map(|x: &Vec<ProcessedNode>| {
x.iter()
.map(|y: &ProcessedNode| (y.x as f64, y.z as f64))
.collect()
})
.collect();
inverse_floodfill(max_x, max_z, outers, inners, editor, ground_level);
@@ -57,8 +65,8 @@ pub fn generate_water_areas(
// Merges ways that share nodes into full loops
fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
let mut removed = vec![];
let mut merged = vec![];
let mut removed: Vec<usize> = vec![];
let mut merged: Vec<Vec<ProcessedNode>> = vec![];
for i in 0..loops.len() {
for j in 0..loops.len() {
@@ -70,8 +78,8 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
continue;
}
let x = &loops[i];
let y = &loops[j];
let x: &Vec<ProcessedNode> = &loops[i];
let y: &Vec<ProcessedNode> = &loops[j];
// it's looped already
if x[0].id == x.last().unwrap().id {
@@ -87,7 +95,7 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
removed.push(i);
removed.push(j);
let mut x = x.clone();
let mut x: Vec<ProcessedNode> = x.clone();
x.reverse();
x.extend(y.iter().skip(1).cloned());
merged.push(x);
@@ -95,7 +103,7 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
removed.push(i);
removed.push(j);
let mut x = x.clone();
let mut x: Vec<ProcessedNode> = x.clone();
x.extend(y.iter().rev().skip(1).cloned());
merged.push(x);
@@ -103,7 +111,7 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
removed.push(i);
removed.push(j);
let mut y = y.clone();
let mut y: Vec<ProcessedNode> = y.clone();
y.extend(x.iter().skip(1).cloned());
merged.push(y);
@@ -117,7 +125,7 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
loops.remove(*r);
}
let merged_len = merged.len();
let merged_len: usize = merged.len();
for m in merged {
loops.push(m);
}
@@ -128,7 +136,7 @@ fn merge_loopy_loops(loops: &mut Vec<Vec<ProcessedNode>>) {
}
fn verify_loopy_loops(loops: &[Vec<ProcessedNode>]) -> bool {
let mut valid = true;
let mut valid: bool = true;
for l in loops {
if l[0].id != l.last().unwrap().id {
eprintln!("WARN: Disconnected loop");
@@ -150,17 +158,17 @@ fn inverse_floodfill(
editor: &mut WorldEditor,
ground_level: i32,
) {
let min_x = 0;
let min_z = 0;
let min_x: i32 = 0;
let min_z: i32 = 0;
let inners: Vec<_> = inners
.into_iter()
.map(|x| Polygon::new(LineString::from(x), vec![]))
.map(|x: Vec<(f64, f64)>| Polygon::new(LineString::from(x), vec![]))
.collect();
let outers: Vec<_> = outers
.into_iter()
.map(|x| Polygon::new(LineString::from(x), vec![]))
.map(|x: Vec<(f64, f64)>| Polygon::new(LineString::from(x), vec![]))
.collect();
inverse_floodfill_recursive(
@@ -193,9 +201,9 @@ fn inverse_floodfill_recursive(
return;
}
let center_x = (min.0 + max.0) / 2;
let center_z = (min.1 + max.1) / 2;
let quadrants = [
let center_x: i32 = (min.0 + max.0) / 2;
let center_z: i32 = (min.1 + max.1) / 2;
let quadrants: [(i32, i32, i32, i32); 4] = [
(min.0, center_x, min.1, center_z),
(center_x, max.0, min.1, center_z),
(min.0, center_x, center_z, max.1),
@@ -203,13 +211,13 @@ fn inverse_floodfill_recursive(
];
for (min_x, max_x, min_z, max_z) in quadrants {
let rect = Rect::new(
let rect: Rect = Rect::new(
Point::new(min_x as f64, min_z as f64),
Point::new(max_x as f64, max_z as f64),
);
if outers.iter().any(|outer| outer.contains(&rect))
&& !inners.iter().any(|inner| inner.intersects(&rect))
if outers.iter().any(|outer: &Polygon| outer.contains(&rect))
&& !inners.iter().any(|inner: &Polygon| inner.intersects(&rect))
{
// every block in rect is water
// so we can safely just set the whole thing to water
@@ -223,7 +231,7 @@ fn inverse_floodfill_recursive(
// This saves on processing time
let outers_intersects: Vec<_> = outers
.iter()
.filter(|poly| poly.intersects(&rect))
.filter(|poly: &&Polygon| poly.intersects(&rect))
.cloned()
.collect();
@@ -231,7 +239,7 @@ fn inverse_floodfill_recursive(
// I assume it changes how the compiler is able to optimize it
let inners_intersects: Vec<_> = inners
.iter()
.filter(|poly| poly.intersects(&rect))
.filter(|poly: &&Polygon| poly.intersects(&rect))
.cloned()
.collect();
@@ -261,10 +269,10 @@ fn inverse_floodfill_iterative(
) {
for x in min.0..max.0 {
for z in min.1..max.1 {
let p = Point::new(x as f64, z as f64);
let p: Point = Point::new(x as f64, z as f64);
if outers.iter().any(|poly| poly.contains(&p))
&& inners.iter().all(|poly| !poly.contains(&p))
if outers.iter().any(|poly: &Polygon| poly.contains(&p))
&& inners.iter().all(|poly: &Polygon| !poly.contains(&p))
{
editor.set_block(WATER, x, ground_level, z, None, None);
}

View File

@@ -13,7 +13,7 @@ pub fn flood_fill_area(
return vec![]; // Not a valid polygon
}
let start_time = Instant::now();
let start_time: Instant = Instant::now();
// Calculate bounding box of the polygon using itertools
let (min_x, max_x) = polygon_coords
@@ -41,8 +41,8 @@ pub fn flood_fill_area(
let polygon: Polygon<f64> = Polygon::new(exterior, vec![]); // Create Polygon using LineString
// Determine safe step sizes for grid sampling
let step_x = ((max_x - min_x) / 10).max(1); // Ensure step is at least 1
let step_z = ((max_z - min_z) / 10).max(1); // Ensure step is at least 1
let step_x: i32 = ((max_x - min_x) / 10).max(1); // Ensure step is at least 1
let step_z: i32 = ((max_z - min_z) / 10).max(1); // Ensure step is at least 1
// Sample multiple starting points within the bounding box
let mut candidate_points: VecDeque<(i32, i32)> = VecDeque::new();

View File

@@ -1,3 +1,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
mod args;
mod block_definitions;
mod bresenham;
@@ -6,6 +8,7 @@ mod data_processing;
mod element_processing;
mod floodfill;
mod osm_parser;
mod progress;
mod retrieve_data;
mod version_check;
mod world_editor;
@@ -13,8 +16,10 @@ mod world_editor;
use args::Args;
use clap::Parser;
use colored::*;
use rfd::FileDialog;
use std::fs::File;
use std::io::Write;
use std::{env, path::PathBuf};
fn print_banner() {
let version: &str = env!("CARGO_PKG_VERSION");
@@ -40,58 +45,211 @@ fn print_banner() {
}
fn main() {
print_banner();
// Parse arguments to decide whether to launch the UI or CLI
let raw_args: Vec<String> = std::env::args().collect();
// Check for updates
if let Err(e) = version_check::check_for_updates() {
eprintln!(
"{}: {}",
"Error checking for version updates".red().bold(),
e
);
// Check if either `--help` or `--path` is present to run command-line mode
let is_help: bool = raw_args.iter().any(|arg: &String| arg == "--help");
let is_path_provided: bool = raw_args
.iter()
.any(|arg: &String| arg.starts_with("--path"));
if is_help || is_path_provided {
print_banner();
// Check for updates
if let Err(e) = version_check::check_for_updates() {
eprintln!(
"{}: {}",
"Error checking for version updates".red().bold(),
e
);
}
// Parse input arguments
let args: Args = Args::parse();
args.run();
let bbox: Vec<f64> = args
.bbox
.as_ref()
.expect("Bounding box is required")
.split(',')
.map(|s: &str| s.parse::<f64>().expect("Invalid bbox coordinate"))
.collect::<Vec<f64>>();
let bbox_tuple: (f64, f64, f64, f64) = (bbox[0], bbox[1], bbox[2], bbox[3]);
// Fetch data
let raw_data: serde_json::Value =
retrieve_data::fetch_data(bbox_tuple, args.file.as_deref(), args.debug, "requests")
.expect("Failed to fetch data");
// Parse raw data
let (mut parsed_elements, scale_factor_x, scale_factor_z) =
osm_parser::parse_osm_data(&raw_data, bbox_tuple, &args);
parsed_elements.sort_by_key(|element: &osm_parser::ProcessedElement| {
osm_parser::get_priority(element)
});
// Write the parsed OSM data to a file for inspection
if args.debug {
let mut output_file: File =
File::create("parsed_osm_data.txt").expect("Failed to create output file");
for element in &parsed_elements {
writeln!(
output_file,
"Element ID: {}, Type: {}, Tags: {:?}",
element.id(),
element.kind(),
element.tags(),
)
.expect("Failed to write to output file");
}
}
// Generate world
let _ =
data_processing::generate_world(parsed_elements, &args, scale_factor_x, scale_factor_z);
} else {
// Launch the UI
println!("Launching UI...");
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
gui_pick_directory,
gui_start_generation,
gui_get_version,
gui_check_for_updates
])
.setup(|app| {
let app_handle = app.handle();
let main_window = tauri::Manager::get_webview_window(app_handle, "main")
.expect("Failed to get main window");
progress::set_main_window(main_window);
Ok(())
})
.run(tauri::generate_context!())
.expect("Error while starting the application UI (Tauri)");
}
}
// Parse input arguments
let args: Args = Args::parse();
args.run();
#[tauri::command]
fn gui_pick_directory() -> Result<String, String> {
// Determine the default Minecraft 'saves' directory based on the OS
let default_dir: Option<PathBuf> = if cfg!(target_os = "windows") {
env::var("APPDATA")
.ok()
.map(|appdata: String| PathBuf::from(appdata).join(".minecraft").join("saves"))
} else if cfg!(target_os = "macos") {
dirs::home_dir().map(|home: PathBuf| {
home.join("Library/Application Support/minecraft")
.join("saves")
})
} else if cfg!(target_os = "linux") {
dirs::home_dir().map(|home: PathBuf| home.join(".minecraft").join("saves"))
} else {
None
};
let bbox: Vec<f64> = args
.bbox
.as_ref()
.expect("Bounding box is required")
.split(',')
.map(|s: &str| s.parse::<f64>().expect("Invalid bbox coordinate"))
.collect::<Vec<f64>>();
// Check if the default directory exists
let starting_directory: Option<PathBuf> = default_dir.filter(|dir: &PathBuf| dir.exists());
let bbox_tuple: (f64, f64, f64, f64) = (bbox[0], bbox[1], bbox[2], bbox[3]);
// Open the directory picker dialog
let dialog: FileDialog = FileDialog::new();
let dialog: FileDialog = if let Some(start_dir) = starting_directory {
dialog.set_directory(start_dir)
} else {
dialog
};
// Fetch data
let raw_data: serde_json::Value =
retrieve_data::fetch_data(bbox_tuple, args.file.as_deref(), args.debug, "requests")
.expect("Failed to fetch data");
if let Some(path) = dialog.pick_folder() {
// Print the full path to the console
println!("Selected world path: {}", path.display());
// Parse raw data
let (mut parsed_elements, scale_factor_x, scale_factor_z) =
osm_parser::parse_osm_data(&raw_data, bbox_tuple, &args);
parsed_elements
.sort_by_key(|element: &osm_parser::ProcessedElement| osm_parser::get_priority(element));
// Write the parsed OSM data to a file for inspection
if args.debug {
let mut output_file: File =
File::create("parsed_osm_data.txt").expect("Failed to create output file");
for element in &parsed_elements {
writeln!(
output_file,
"Element ID: {}, Type: {}, Tags: {:?}",
element.id(),
element.kind(),
element.tags(),
)
.expect("Failed to write to output file");
// Check if the "region" folder exists within the selected directory
if path.join("region").exists() {
return Ok(path.display().to_string());
} else {
// Notify the frontend that no valid Minecraft world was found
return Err("Invalid Minecraft world".to_string());
}
}
// Generate world
data_processing::generate_world(parsed_elements, &args, scale_factor_x, scale_factor_z);
// If no folder was selected, return an error message
Err("No world selected".to_string())
}
#[tauri::command]
fn gui_get_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[tauri::command]
fn gui_check_for_updates() -> Result<bool, String> {
match version_check::check_for_updates() {
Ok(is_newer) => Ok(is_newer),
Err(e) => Err(format!("Error checking for updates: {}", e)),
}
}
#[tauri::command]
fn gui_start_generation(bbox_text: String, selected_world: String) -> Result<(), String> {
tauri::async_runtime::spawn(async move {
if let Err(e) = tokio::task::spawn_blocking(move || {
// Utility function to reorder bounding box coordinates
fn reorder_bbox(bbox: &[f64]) -> (f64, f64, f64, f64) {
(bbox[1], bbox[0], bbox[3], bbox[2])
}
// Parse bounding box string and validate it
let bbox: Vec<f64> = bbox_text
.split_whitespace()
.map(|s| s.parse::<f64>().expect("Invalid bbox coordinate"))
.collect();
if bbox.len() != 4 {
return Err("Invalid bounding box format".to_string());
}
// Create an Args instance with the chosen bounding box and world directory path
let args: Args = Args {
bbox: Some(bbox_text),
file: None,
path: selected_world,
downloader: "requests".to_string(),
scale: 1.0,
debug: false,
timeout: None,
};
// Reorder bounding box coordinates for further processing
let reordered_bbox: (f64, f64, f64, f64) = reorder_bbox(&bbox);
// Run data fetch and world generation
match retrieve_data::fetch_data(reordered_bbox, None, args.debug, "requests") {
Ok(raw_data) => {
let (mut parsed_elements, scale_factor_x, scale_factor_z) =
osm_parser::parse_osm_data(&raw_data, reordered_bbox, &args);
parsed_elements.sort_by_key(|element: &osm_parser::ProcessedElement| {
osm_parser::get_priority(element)
});
let _ = data_processing::generate_world(
parsed_elements,
&args,
scale_factor_x,
scale_factor_z,
);
Ok(())
}
Err(e) => Err(format!("Failed to start generation: {}", e)),
}
})
.await
{
eprintln!("Error in blocking task: {}", e);
}
});
Ok(())
}

View File

@@ -1,4 +1,4 @@
use crate::args::Args;
use crate::{args::Args, progress::emit_gui_progress_update};
use colored::Colorize;
use serde::Deserialize;
use serde_json::Value;
@@ -39,7 +39,7 @@ pub struct ProcessedNode {
pub id: u64,
pub tags: HashMap<String, String>,
// minecraft coords
// Minecraft coordinates
pub x: i32,
pub z: i32,
}
@@ -138,6 +138,7 @@ pub fn parse_osm_data(
args: &Args,
) -> (Vec<ProcessedElement>, f64, f64) {
println!("{} Parsing data...", "[2/5]".bold());
emit_gui_progress_update(5.0, "Parsing data...");
// Deserialize the JSON data into the OSMData structure
let data: OsmData =
@@ -145,16 +146,16 @@ pub fn parse_osm_data(
// Determine which dimension is larger and assign scale factors accordingly
let (scale_factor_z, scale_factor_x) = geo_distance(bbox.1, bbox.3, bbox.0, bbox.2);
let scale_factor_z = scale_factor_z.floor() * args.scale;
let scale_factor_x = scale_factor_x.floor() * args.scale;
let scale_factor_z: f64 = scale_factor_z.floor() * args.scale;
let scale_factor_x: f64 = scale_factor_x.floor() * args.scale;
if args.debug {
println!("Scale factor X: {}", scale_factor_x);
println!("Scale factor Z: {}", scale_factor_z);
}
let mut nodes_map = HashMap::new();
let mut ways_map = HashMap::new();
let mut nodes_map: HashMap<u64, ProcessedNode> = HashMap::new();
let mut ways_map: HashMap<u64, ProcessedWay> = HashMap::new();
let mut processed_elements: Vec<ProcessedElement> = Vec::new();
@@ -165,7 +166,7 @@ pub fn parse_osm_data(
let (x, z) =
lat_lon_to_minecraft_coords(lat, lon, bbox, scale_factor_z, scale_factor_x);
let processed = ProcessedNode {
let processed: ProcessedNode = ProcessedNode {
id: element.id,
tags: element.tags.clone().unwrap_or_default(),
x,
@@ -190,7 +191,7 @@ pub fn parse_osm_data(
continue;
}
let mut nodes = vec![];
let mut nodes: Vec<ProcessedNode> = vec![];
if let Some(node_ids) = &element.nodes {
for &node_id in node_ids {
if let Some(node) = nodes_map.get(&node_id) {
@@ -199,7 +200,7 @@ pub fn parse_osm_data(
}
}
let processed = ProcessedWay {
let processed: ProcessedWay = ProcessedWay {
id: element.id,
tags: element.tags.clone().unwrap_or_default(),
nodes,
@@ -223,14 +224,14 @@ pub fn parse_osm_data(
};
// Only process multipolygons for now
if tags.get("type").map(|x| x.as_str()) != Some("multipolygon") {
if tags.get("type").map(|x: &String| x.as_str()) != Some("multipolygon") {
continue;
};
let members = element
let members: Vec<ProcessedMember> = element
.members
.iter()
.filter_map(|mem| {
.filter_map(|mem: &OsmMember| {
if mem.r#type != "way" {
eprintln!("WARN: Unknown relation type {}", mem.r#type);
return None;
@@ -247,7 +248,7 @@ pub fn parse_osm_data(
}
};
let way = ways_map
let way: ProcessedWay = ways_map
.get(&mem.r#ref)
.expect("Missing a way referenced by a rel")
.clone();
@@ -263,6 +264,8 @@ pub fn parse_osm_data(
}));
}
emit_gui_progress_update(10.0, "");
(processed_elements, scale_factor_x, scale_factor_z)
}
@@ -284,10 +287,10 @@ pub fn get_priority(element: &ProcessedElement) -> usize {
// (lat meters, lon meters)
fn geo_distance(lat1: f64, lat2: f64, lon1: f64, lon2: f64) -> (f64, f64) {
let z = lat_distance(lat1, lat2);
let z: f64 = lat_distance(lat1, lat2);
// distance between two lons depends on their latitude. In this case we'll just average them
let x = lon_distance((lat1 + lat2) / 2.0, lon1, lon2);
let x: f64 = lon_distance((lat1 + lat2) / 2.0, lon1, lon2);
(z, x)
}
@@ -296,10 +299,10 @@ fn geo_distance(lat1: f64, lat2: f64, lon1: f64, lon2: f64) -> (f64, f64) {
// returns meters
fn lon_distance(lat: f64, lon1: f64, lon2: f64) -> f64 {
const R: f64 = 6_371_000.0;
let d_lon = (lon2 - lon1).to_radians();
let a =
let d_lon: f64 = (lon2 - lon1).to_radians();
let a: f64 =
lat.to_radians().cos() * lat.to_radians().cos() * (d_lon / 2.0).sin() * (d_lon / 2.0).sin();
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
let c: f64 = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
R * c
}
@@ -308,9 +311,9 @@ fn lon_distance(lat: f64, lon1: f64, lon2: f64) -> f64 {
// returns meters
fn lat_distance(lat1: f64, lat2: f64) -> f64 {
const R: f64 = 6_371_000.0;
let d_lat = (lat2 - lat1).to_radians();
let a = (d_lat / 2.0).sin() * (d_lat / 2.0).sin();
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
let d_lat: f64 = (lat2 - lat1).to_radians();
let a: f64 = (d_lat / 2.0).sin() * (d_lat / 2.0).sin();
let c: f64 = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
R * c
}

46
src/progress.rs Normal file
View File

@@ -0,0 +1,46 @@
use once_cell::sync::OnceCell;
use serde_json::json;
use tauri::{Emitter, WebviewWindow};
pub static MAIN_WINDOW: OnceCell<WebviewWindow> = OnceCell::new();
pub fn set_main_window(window: WebviewWindow) {
MAIN_WINDOW.set(window).ok();
}
pub fn get_main_window() -> Option<&'static WebviewWindow> {
MAIN_WINDOW.get()
}
/// This function checks if the program is running with a GUI window.
/// Returns `true` if a GUI window is initialized, `false` otherwise.
pub fn is_running_with_gui() -> bool {
get_main_window().is_some()
}
/// This code manages a multi-step process with a progress bar indicating the overall completion.
/// The progress updates are mapped to specific steps in the pipeline:
///
/// [1/5] Fetching data... - Starts at: 0% / Completes at: 5%
/// [2/5] Parsing data... - Starts at: 5% / Completes at: 10%
/// [3/5] Processing data... - Starts at: 10% / Completes at: 60%
/// [4/5] Generating ground layer... - Starts at: 60% / Completes at: 90%
/// [5/5] Saving world... - Starts at: 90% / Completes at: 100%
///
/// The function `emit_gui_progress_update` is used to send real-time progress updates to the UI.
pub fn emit_gui_progress_update(progress: f64, message: &str) {
if let Some(window) = get_main_window() {
let payload = json!({
"progress": progress,
"message": message
});
if let Err(e) = window.emit("progress-update", payload) {
eprintln!("Failed to emit progress event: {}", e);
}
}
}
pub fn emit_gui_error(message: &str) {
emit_gui_progress_update(0.0, &format!("Error! {}", message));
}

View File

@@ -1,3 +1,4 @@
use crate::progress::{emit_gui_error, emit_gui_progress_update, is_running_with_gui};
use colored::Colorize;
use rand::seq::SliceRandom;
use reqwest::blocking::Client;
@@ -14,7 +15,8 @@ fn download_with_reqwest(url: &str, query: &str) -> Result<String, Box<dyn std::
.timeout(Duration::from_secs(1800))
.build()?;
let response = client.get(url).query(&[("data", query)]).send();
let response: Result<reqwest::blocking::Response, reqwest::Error> =
client.get(url).query(&[("data", query)]).send();
match response {
Ok(resp) => {
@@ -32,10 +34,17 @@ fn download_with_reqwest(url: &str, query: &str) -> Result<String, Box<dyn std::
.red()
.bold()
);
emit_gui_error("Request timed out. Try selecting a smaller area.");
} else {
eprintln!("{}", format!("Error! {}", e).red().bold());
emit_gui_error(&e.to_string());
}
if !is_running_with_gui() {
std::process::exit(1);
} else {
Ok("".to_string())
}
std::process::exit(1);
}
}
}
@@ -76,6 +85,7 @@ pub fn fetch_data(
download_method: &str,
) -> Result<Value, Box<dyn std::error::Error>> {
println!("{} Fetching data...", "[1/5]".bold());
emit_gui_progress_update(1.0, "Fetching data...");
// List of Overpass API servers
let api_servers: Vec<&str> = vec![
@@ -146,22 +156,33 @@ pub fn fetch_data(
// Check if the remark mentions memory or other runtime errors
if remark.contains("runtime error") && remark.contains("out of memory") {
eprintln!("{}", "Error! The query ran out of memory on the Overpass API server. Try using a smaller area.".red().bold());
emit_gui_error("Try using a smaller area.");
} else {
// Handle other Overpass API errors if present in the remark field
eprintln!(
"{}",
format!("Error! API returned: {}", remark).red().bold()
);
emit_gui_error(&format!("API returned: {}", remark));
}
} else {
// General case for when there are no elements and no specific remark
eprintln!("{}", "Error! No data available.".red().bold());
eprintln!(
"{}",
"Error! No data available in this region.".red().bold()
);
emit_gui_error("No data available in this region.");
}
if debug {
println!("Additional debug information: {}", data);
}
std::process::exit(1);
if !is_running_with_gui() {
std::process::exit(1);
} else {
return Err("Data fetch failed".into());
}
}
// If debug is enabled, write data to file
@@ -170,6 +191,8 @@ pub fn fetch_data(
file.write_all(response.as_bytes())?;
}
emit_gui_progress_update(5.0, "");
Ok(data)
}
}

View File

@@ -9,8 +9,8 @@ const REMOTE_CARGO_TOML_URL: &str =
"https://raw.githubusercontent.com/louis-e/arnis/main/Cargo.toml";
/// Fetches the latest version from the remote Cargo.toml file and compares it with the local version.
/// If a newer version is available, prints a message.
pub fn check_for_updates() -> Result<(), Box<dyn Error>> {
/// Returns `true` if a newer version is available, `false` otherwise.
pub fn check_for_updates() -> Result<bool, Box<dyn Error>> {
let client: Client = Client::new();
// Fetch the remote Cargo.toml file with a User-Agent header
@@ -24,7 +24,7 @@ pub fn check_for_updates() -> Result<(), Box<dyn Error>> {
// If the response status is not 200 OK, handle it as an HTTP error
if !res.status().is_success() {
handle_http_error(res.status());
return Ok(());
return Ok(false);
}
let response_text: String = res.text()?;
@@ -40,12 +40,16 @@ pub fn check_for_updates() -> Result<(), Box<dyn Error>> {
local_version,
remote_version
);
return Ok(true); // Newer version is available
}
}
Err(err) => handle_request_error(err),
}
Ok(())
Ok(false) // Local version is up-to-date
}
Err(err) => {
handle_request_error(err);
Ok(false) // Treat request failures as no new version available
}
}
}
/// Extracts the version from the contents of a Cargo.toml file.

View File

@@ -1,5 +1,6 @@
use crate::args::Args;
use crate::block_definitions::*;
use crate::progress::emit_gui_progress_update;
use colored::Colorize;
use fastanvil::Region;
use fastnbt::{LongArray, Value};
@@ -192,11 +193,11 @@ impl WorldToModify {
fn get_block(&self, x: i32, y: i32, z: i32) -> Option<Block> {
let chunk_x: i32 = x >> 4;
let chunk_z: i32 = z >> 4;
let region_x = chunk_x >> 5;
let region_z = chunk_z >> 5;
let region_x: i32 = chunk_x >> 5;
let region_z: i32 = chunk_z >> 5;
let region = self.get_region(region_x, region_z)?;
let chunk = region.get_chunk(chunk_x & 31, chunk_z & 31)?;
let region: &RegionToModify = self.get_region(region_x, region_z)?;
let chunk: &ChunkToModify = region.get_chunk(chunk_x & 31, chunk_z & 31)?;
chunk.get_block(
(x & 15).try_into().unwrap(),
@@ -208,11 +209,11 @@ impl WorldToModify {
fn set_block(&mut self, x: i32, y: i32, z: i32, block: Block) {
let chunk_x: i32 = x >> 4;
let chunk_z: i32 = z >> 4;
let region_x = chunk_x >> 5;
let region_z = chunk_z >> 5;
let region_x: i32 = chunk_x >> 5;
let region_z: i32 = chunk_z >> 5;
let region = self.get_or_create_region(region_x, region_z);
let chunk = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31);
let region: &mut RegionToModify = self.get_or_create_region(region_x, region_z);
let chunk: &mut ChunkToModify = region.get_or_create_chunk(chunk_x & 31, chunk_z & 31);
chunk.set_block(
(x & 15).try_into().unwrap(),
@@ -249,7 +250,7 @@ impl<'a> WorldEditor<'a> {
const REGION_TEMPLATE: &[u8] = include_bytes!("region.template");
let mut region_file = File::options()
let mut region_file: File = File::options()
.read(true)
.write(true)
.create(true)
@@ -268,9 +269,10 @@ impl<'a> WorldEditor<'a> {
(self.scale_factor_x as i32, self.scale_factor_x as i32)
}
pub fn block_at(&self, x: i32, y: i32, z: i32) -> bool {
// Unused and not tested
/*pub fn block_at(&self, x: i32, y: i32, z: i32) -> bool {
self.world.get_block(x, y, z).is_some()
}
}*/
/// Sets a block of the specified type at the given coordinates.
pub fn set_block(
@@ -292,11 +294,11 @@ impl<'a> WorldEditor<'a> {
if let Some(whitelist) = override_whitelist {
whitelist
.iter()
.any(|whitelisted_block| whitelisted_block.id() == existing_block.id())
.any(|whitelisted_block: &Block| whitelisted_block.id() == existing_block.id())
} else if let Some(blacklist) = override_blacklist {
!blacklist
.iter()
.any(|blacklisted_block| blacklisted_block.id() == existing_block.id())
.any(|blacklisted_block: &Block| blacklisted_block.id() == existing_block.id())
} else {
false
}
@@ -351,7 +353,7 @@ impl<'a> WorldEditor<'a> {
if let Some(whitelist) = whitelist {
if whitelist
.iter()
.any(|whitelisted_block| whitelisted_block.id() == existing_block.id())
.any(|whitelisted_block: &Block| whitelisted_block.id() == existing_block.id())
{
return true; // Block is in whitelist
}
@@ -359,7 +361,7 @@ impl<'a> WorldEditor<'a> {
if let Some(blacklist) = blacklist {
if blacklist
.iter()
.any(|blacklisted_block| blacklisted_block.id() == existing_block.id())
.any(|blacklisted_block: &Block| blacklisted_block.id() == existing_block.id())
{
return true; // Block is in blacklist
}
@@ -372,6 +374,7 @@ impl<'a> WorldEditor<'a> {
/// Saves all changes made to the world by writing modified chunks to the appropriate region files.
pub fn save(&mut self) {
println!("{} Saving world...", "[5/5]".bold());
emit_gui_progress_update(90.0, "Saving world...");
let _debug: bool = self.args.debug;
let total_regions: u64 = self.world.regions.len() as u64;
@@ -386,12 +389,17 @@ impl<'a> WorldEditor<'a> {
.progress_chars("█▓░"),
);
let total_steps: f64 = 10.0;
let progress_increment_save: f64 = total_steps / total_regions as f64;
let mut current_progress_save: f64 = 90.0;
let mut last_emitted_progress: f64 = current_progress_save;
for ((region_x, region_z), region_to_modify) in &self.world.regions {
let mut region = self.create_region(*region_x, *region_z);
let mut region: Region<File> = self.create_region(*region_x, *region_z);
for chunk_x in 0..32 {
for chunk_z in 0..32 {
let data = region
let data: Vec<u8> = region
.read_chunk(chunk_x as usize, chunk_z as usize)
.unwrap()
.unwrap();
@@ -418,6 +426,12 @@ impl<'a> WorldEditor<'a> {
}
save_pb.inc(1);
current_progress_save += progress_increment_save;
if (current_progress_save - last_emitted_progress).abs() > 0.25 {
emit_gui_progress_update(current_progress_save, "Saving world...");
last_emitted_progress = current_progress_save;
}
}
save_pb.finish();

36
tauri.conf.json Normal file
View File

@@ -0,0 +1,36 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Arnis",
"version": "2.1.0",
"identifier": "com.louisdev.arnis",
"build": {
"frontendDist": "gui-src"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "Arnis",
"width": 1000,
"height": 650,
"resizable": false,
"transparent": true,
"center": true
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}