1
0
forked from MTSR/mapserver

Merge branch 'master' into threejs-wip

This commit is contained in:
NatureFreshMilk 2019-12-13 10:41:46 +01:00
commit 467429ace8
84 changed files with 969 additions and 650 deletions

19
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: docker
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: docker publish
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: minetestmapserver/mapserver
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
tag_names: true
cache: true

24
.github/workflows/go-test.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: go-test
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-go@v1
with:
go-version: '1.12'
- name: get
run: go get github.com/mjibson/esc
- name: generate
run: go generate
- name: test
run: go test ./...

20
.github/workflows/jshint.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: jshint
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: apt
run: sudo apt-get install -y nodejs npm
- name: jshint install
run: sudo npm i -g jshint
- name: jshint run
run: cd static/js && jshint

22
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: release
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: build
run: make release
- name: upload
uses: skx/github-action-publish-binaries@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: 'output/mapserver-*'

View File

@ -1,14 +0,0 @@
language: go
sudo: false
go:
- 1.11.x
os:
- linux
script:
- go get github.com/mjibson/esc
- go generate
- go build
- go test

View File

@ -1,22 +1,17 @@
FROM ubuntu as builder
FROM golang:1.13-alpine as builder
RUN apt-get update &&\
apt-get install -y software-properties-common git &&\
add-apt-repository ppa:longsleep/golang-backports &&\
apt-get update &&\
apt-get install -y golang-go
RUN apk --no-cache add ca-certificates gcc libc-dev nodejs npm git make
VOLUME /root/go
COPY ./ /server
RUN cd /server &&\
go generate &&\
go test ./... &&\
CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static -X mapserver/app.Version=docker"
npm install -g jshint rollup &&\
make test jshint all
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /server/mapserver /bin/mapserver
COPY --from=builder /server/output/mapserver-linux-x86_64 /bin/mapserver
EXPOSE 8080
CMD ["/bin/mapserver"]

View File

@ -15,9 +15,9 @@ BINARIES += $(OUT_DIR)/mapserver-windows-x86.exe
BINARIES += $(OUT_DIR)/mapserver-windows-x86-64.exe
BINARIES += $(OUT_DIR)/mapserver-linux-arm
JS_BUNDLE = static/js/bundle.js
all: $(STATIC_VFS)
go build
all: $(STATIC_VFS) $(OUT_DIR)/mapserver-linux-x86_64
$(OUT_DIR):
mkdir $@
@ -27,23 +27,25 @@ fmt:
test: $(OUT_DIR)
go generate
go build
go vet ./...
$(ENV) go test ./...
clean:
rm -rf $(STATIC_VFS) test-output
rm -rf $(STATIC_VFS) $(JS_BUNDLE) test-output
rm -rf $(OUT_DIR)
jshint:
jshint static/js/*.js static/js/util static/js/overlays static/js/search
cd static/js && jshint .
$(STATIC_VFS):
$(JS_BUNDLE):
cd static/js && rollup -c rollup.config.js
$(STATIC_VFS): $(JS_BUNDLE)
go generate
$(OUT_DIR)/mapserver-linux-x86_64: $(OUT_DIR)
# native (linux x86_64)
GOOS=linux GOARCH=amd64 CC=x86_64-linux-gnu-gcc $(GO_BUILD) $(GO_LDFLAGS) -o $@
GOOS=linux GOARCH=amd64 CC=gcc $(GO_BUILD) $(GO_LDFLAGS) -o $@
$(OUT_DIR)/mapserver-linux-x86: $(OUT_DIR)
# apt install gcc-8-i686-linux-gnu
@ -63,11 +65,11 @@ $(OUT_DIR)/mapserver-linux-arm: $(OUT_DIR)
release: builder_image $(OUT_DIR) $(MOD_ZIP)
# build all with the docker image
sudo docker run --rm -it\
-v $(shell pwd):/app\
-v mapserver-volume:/root/go\
-w /app\
mapserver-builder\
sudo docker run --rm -i \
-v $(shell pwd):/app \
-v mapserver-volume:/root/go \
-w /app \
mapserver-builder \
make test jshint release-all VERSION=$(VERSION)
# copy generated files to output dir

View File

@ -86,10 +86,10 @@ func Setup(p params.ParamsType, cfg *Config) *App {
colorfiles := []string{
//https://daconcepts.com/vanessa/hobbies/minetest/colors.txt
"/colors/custom.txt",
"/colors/vanessa.txt",
"/colors/advtrains.txt",
"/colors/scifi_nodes.txt",
"/colors/custom.txt",
}
for _, colorfile := range colorfiles {

View File

@ -4,6 +4,10 @@
## Next
* Train overlay decluttering
* Updated builtin colors
* Hide travelnets and missions by pattern "(P)"
* Added benchmark
* Minor speed improvements
## 3.0.0

7
doc/get_trainlines.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
curl -X POST -k -H 'Content-Type: application/json' 'https://pandorabox.io/map/api/mapobjects/' --data '{
"type":"train",
"pos1":{"x":-2048,"y":-2048,"z":-2048},
"pos2":{"x":2048,"y":2048,"z":2048}
}' | jq

View File

@ -3,6 +3,8 @@
**Please make a backup of your world in case something goes wrong**
## Simple installation
* Download the binary from the [releases](https://github.com/minetest-tools/mapserver/releases) for your architecture and platform
* Drop the binary into your world folder (the one with the `world.mt` and `map.sqlite` files)
* Start the mapserver via command-line: `./mapserver` or `./mapserver.exe`
@ -10,6 +12,36 @@
For additional infos (lag,time,players => active mode) on the mapserver interface you should install the [mapserver-mod](mod.md)
## Docker image
* Docker-hub: https://hub.docker.com/repository/docker/minetestmapserver/mapserver
Simple docker run example to run in the world-directory:
```
docker run --rm --it -p 8080:8080 -v $(pwd):/minetest -w /minetest minetestmapserver/mapserver
```
## Docker compose
Examplary `docker-compose` config:
```yml
services:
mapserver:
image: minetesttools/mapserver
restart: always
networks:
- default
depends_on:
- "postgres"
volumes:
- "./data/minetest/world:/minetest"
working_dir: "/minetest"
```
* See also: https://github.com/pandorabox-io/pandorabox.io/blob/master/docker-compose.yml
## Performance / Scalability
For small to medium setups the default values should suffice.

76
doc/recommended_specs.md Normal file
View File

@ -0,0 +1,76 @@
# Recommended specs
Recommended specs for running the mapserver
## Storage
The tiles are cached in several zoom-levels on disk.
Storage usage depends heavily on map-size and explored areas
but it will be in the region of several gigabytes (5 to 10 GB for "older" servers)
## Memory
Memory depedends on the amount of caching (see: [Configuration](./config.md))
Per default there will be around 500 mapblocks cached for quicker access.
This will be around 5 to 10 megabytes depending on the contents.
The recommendation is to maximize caching so the queries don't slow down the game-database.
If you are willing to spend around 2 GB of memory set the `maxitems` to 5000 mapblocks.
Otherwise leave the defaults and see if it has any impact.
Example config from `mapserver.json`:
```json
{
"mapblockaccessor": {
"expiretime": "10s",
"purgetime": "15s",
"maxitems": 500
}
}
```
## CPU
The application is more CPU-bound than IO-bound.
It will use all the configured CPU's while rendering:
<img src="../pics/cpu_graph.png"/>
It is recommended to set the `renderjobs` setting to a number of CPU's
you can spare.
Per default it will set the setting to the number of cores you have,
but it will also run on just 1 job and take a bit longer...
Example config from `mapserver.json`:
```json
{
"renderingjobs": 2
}
```
## Database
The recommended database is Postgres if you have a busy server.
SQLite will work too if the disk-access is good.
**Personal experience**: Don't worry about it if you have fast SSD's ;)
## Network
I don't recommend serving the map behind a slow residential internet connection.
An upload-bandwidth from 10 MBPS upwards will do the job pretty ok though:
<img src="../pics/network_graph.png"/>
# Recap
If your setup looks something like this, don't worry about performance:
* 4+ cores
* 8+ GB RAM
* 20+ GB SSD
* Postgres DB

View File

@ -10,10 +10,9 @@ RUN add-apt-repository ppa:longsleep/golang-backports &&\
apt-get update &&\
apt-get install -y golang-go
# luacheck
RUN apt-get install -y luarocks liblua5.1-dev
RUN luarocks install luacheck
# jshint
RUN apt-get install -y nodejs npm
RUN npm install -g jshint
# rollup
RUN npm install -g rollup

17
go.mod
View File

@ -1,15 +1,16 @@
module mapserver
require (
github.com/disintegration/imaging v1.5.0
github.com/gorilla/websocket v1.4.0
github.com/lib/pq v1.0.0
github.com/mattn/go-sqlite3 v1.10.0
github.com/disintegration/imaging v1.6.2
github.com/gorilla/websocket v1.4.1
github.com/lib/pq v1.2.0
github.com/mattn/go-sqlite3 v1.13.0
github.com/mjibson/esc v0.1.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/prometheus/client_golang v0.9.2
github.com/sirupsen/logrus v1.3.0
github.com/stretchr/testify v1.2.2
github.com/prometheus/client_golang v0.9.4
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.4.0
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583
golang.org/x/image v0.0.0-20190118043309-183bebdce1b2 // indirect
)
go 1.13

65
go.sum
View File

@ -1,54 +1,119 @@
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.5.0 h1:uYqUhwNmLU4K1FN44vhqS4TZJRAA4RhBINgbQlKyGi0=
github.com/disintegration/imaging v1.5.0/go.mod h1:9B/deIUIrliYkyMTuXJd6OUFLcrZ2tf+3Qlwnaf/CjU=
github.com/disintegration/imaging v1.6.1 h1:JnBbK6ECIZb1NsWIikP9pd8gIlTIRx7fuDNpU9fsxOE=
github.com/disintegration/imaging v1.6.1/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do=
github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mjibson/esc v0.1.0 h1:5ch+murgrcwDFLOE2hwj0f7kE4xJfJhkSCAjSLY182o=
github.com/mjibson/esc v0.1.0/go.mod h1:9Hw9gxxfHulMF5OJKCyhYD7PzlSdhzXyaGEBRPH1OPs=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A=
github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583 h1:SZPG5w7Qxq7bMcMVl6e3Ht2X7f+AAGQdzjkbyOnNNZ8=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190118043309-183bebdce1b2 h1:FNSSV4jv1PrPsiM2iKGpqLPPgYACqh9Muav7Pollk1k=
golang.org/x/image v0.0.0-20190118043309-183bebdce1b2/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -66,7 +66,7 @@ func getNodePos(x, y, z int) int {
}
func (inv *Inventory) IsEmpty() bool {
if inv.Size == 0 || len(inv.Items) == 0 {
if len(inv.Items) == 0 {
return true
}

View File

@ -155,6 +155,7 @@ func parseMetadata(mapblock *MapBlock, data []byte) (int, error) {
}
currentInventory.Items = append(currentInventory.Items, &item)
currentInventory.Size += 1
}

View File

@ -4,7 +4,6 @@ import (
"errors"
"image"
"image/color"
"image/draw"
"mapserver/colormapping"
"mapserver/coords"
"mapserver/mapblockaccessor"
@ -69,7 +68,7 @@ func addColorComponent(c *color.RGBA, value int) *color.RGBA {
R: clamp(int(c.R) + value),
G: clamp(int(c.G) + value),
B: clamp(int(c.B) + value),
A: clamp(int(c.A) + value),
A: c.A,
}
}
@ -180,7 +179,7 @@ func (r *MapBlockRenderer) Render(pos1, pos2 *coords.MapBlockCoords) (*image.NRG
if neighbourMapblock != nil && err == nil {
top = neighbourMapblock.GetNodeName(x, y, 0)
if y < 15 {
topAbove = neighbourMapblock.GetNodeName(x, y+1, z+0)
topAbove = neighbourMapblock.GetNodeName(x, y+1, 0)
}
}
}
@ -209,10 +208,21 @@ func (r *MapBlockRenderer) Render(pos1, pos2 *coords.MapBlockCoords) (*image.NRG
imgX := x * IMG_SCALE
imgY := (15 - z) * IMG_SCALE
rect := image.Rect(
imgX, imgY,
imgX+IMG_SCALE, imgY+IMG_SCALE,
)
r32, g32, b32, a32 := c.RGBA()
r8, g8, b8, a8 := uint8(r32), uint8(g32), uint8(b32), uint8(a32)
for Y := imgY; Y < imgY+IMG_SCALE; Y++ {
ix := (Y*IMG_SIZE + imgX) << 2
for X := 0; X < IMG_SCALE; X++ {
img.Pix[ix] = r8
ix++
img.Pix[ix] = g8
ix++
img.Pix[ix] = b8
ix++
img.Pix[ix] = a8
ix++
}
}
if c.A != 0xFF || !r.enableTransparency {
//not transparent, mark as rendered
@ -220,8 +230,6 @@ func (r *MapBlockRenderer) Render(pos1, pos2 *coords.MapBlockCoords) (*image.NRG
xzOccupationMap[x][z] = true
}
draw.Draw(img, rect, &image.Uniform{c}, image.ZP, draw.Src)
if foundBlocks == EXPECTED_BLOCKS_PER_FLAT_MAPBLOCK {
return img, nil
}

View File

@ -3,6 +3,7 @@ package mapobject
import (
"mapserver/mapblockparser"
"mapserver/mapobjectdb"
"strconv"
)
type BonesBlock struct{}
@ -10,13 +11,24 @@ type BonesBlock struct{}
func (this *BonesBlock) onMapObject(x, y, z int, block *mapblockparser.MapBlock) *mapobjectdb.MapObject {
md := block.Metadata.GetMetadata(x, y, z)
if md["owner"] == "" {
invMap := block.Metadata.GetInventoryMapAtPos(x, y, z)
mainInv := invMap["main"]
if mainInv == nil {
return nil
}
o := mapobjectdb.NewMapObject(block.Pos, x, y, z, "bones")
o.Attributes["time"] = md["time"]
o.Attributes["owner"] = md["owner"]
o.Attributes["info"] = md["infotext"]
itemCount := 0
for _, item := range mainInv.Items {
itemCount += item.Count
}
o.Attributes["item_count"] = strconv.Itoa(itemCount)
return o
}

View File

@ -19,16 +19,11 @@ func (this *FancyVend) onMapObject(x, y, z int, block *mapblockparser.MapBlock)
parser := luaparser.New()
isAdmin := false
isDepositor := false
if nodename == "fancy_vend:admin_vendor" || nodename == "fancy_vend:admin_depo" {
isAdmin = true
}
if nodename == "fancy_vend:player_depo" || nodename == "fancy_vend:admin_depo" {
isDepositor = true
}
payInv := invMap["wanted_item"]
giveInv := invMap["given_item"]
mainInv := invMap["main"]
@ -93,19 +88,11 @@ func (this *FancyVend) onMapObject(x, y, z int, block *mapblockparser.MapBlock)
o.Attributes["owner"] = md["owner"]
o.Attributes["type"] = "fancyvend"
if !isDepositor {
o.Attributes["in_item"] = in_item
o.Attributes["in_count"] = strconv.Itoa(in_count)
o.Attributes["out_item"] = out_item
o.Attributes["out_count"] = strconv.Itoa(out_count)
} else {
// invert in and out
o.Attributes["out_item"] = in_item
o.Attributes["out_count"] = strconv.Itoa(in_count)
o.Attributes["in_item"] = out_item
o.Attributes["in_count"] = strconv.Itoa(out_count)
o.Attributes["in_item"] = in_item
o.Attributes["in_count"] = strconv.Itoa(in_count)
o.Attributes["out_item"] = out_item
o.Attributes["out_count"] = strconv.Itoa(out_count)
}
o.Attributes["stock"] = strconv.Itoa(stock_factor)
return o

View File

@ -15,6 +15,7 @@ func (this *SmartShopBlock) onMapObject(x, y, z int, block *mapblockparser.MapBl
md := block.Metadata.GetMetadata(x, y, z)
invMap := block.Metadata.GetInventoryMapAtPos(x, y, z)
mainInv := invMap["main"]
isCreative := md["type"] == "0"
if mainInv.IsEmpty() {
return list
@ -27,7 +28,7 @@ func (this *SmartShopBlock) onMapObject(x, y, z int, block *mapblockparser.MapBl
pay := invMap[payInvName]
give := invMap[giveInvName]
if pay.IsEmpty() || give.IsEmpty() {
if len(pay.Items) == 0 || len(give.Items) == 0 {
continue
}
@ -43,10 +44,16 @@ func (this *SmartShopBlock) onMapObject(x, y, z int, block *mapblockparser.MapBl
stock := 0
for _, item := range mainInv.Items {
if item.Name == out_item {
stock += item.Count
if isCreative {
stock = 999
} else {
for _, item := range mainInv.Items {
if item.Name == out_item {
stock += item.Count
}
}
}
//multiples of out_count

BIN
pics/cpu_graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
pics/network_graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -1,13 +1,15 @@
Minetest mapserver
=======
<img src="./doc/pics/General_map_preview.png">
[![Dependencies](https://img.shields.io/librariesio/github/minetest-tools/mapserver.svg)](https://github.com/minetest-tools/mapserver)
[![Build Status](https://travis-ci.org/minetest-tools/mapserver.svg?branch=master)](https://travis-ci.org/minetest-tools/mapserver)
![](https://github.com/minetest-mapserver/mapserver/workflows/jshint/badge.svg)
![](https://github.com/minetest-mapserver/mapserver/workflows/go-test/badge.svg)
![GitHub repo size](https://img.shields.io/github/repo-size/minetest-tools/mapserver.svg)
![GitHub closed issues](https://img.shields.io/github/issues-closed/minetest-tools/mapserver.svg)
<img src="./doc/pics/General_map_preview.png">
Realtime mapserver for [Minetest](https://minetest.net)
Demo: [Pandorabox Server map](https://pandorabox.io/map/#-1782.25/493.5/10)
@ -20,6 +22,7 @@ Demo: [Pandorabox Server map](https://pandorabox.io/map/#-1782.25/493.5/10)
* [Parameters](doc/params.md)
* [Search](doc/search.md)
* [Configuration](doc/config.md)
* [Recommended specs](doc/recommended_specs.md)
* [Contribution](doc/contrib.md)
* [Development](doc/dev.md)
* [License](doc/license.md)
@ -53,6 +56,7 @@ See: [Incremental rendering](doc/incrementalrendering.md)
## Planned Features
* Isometric view
* Skin support
* Route planning (via travelnets / trains)
# Supported map-databases

View File

@ -4,4 +4,13 @@ planetoidgen:sun 255 100 0
# beacon
beacon:redbeam 255 0 0
beacon:greenbeam 0 255 0
beacon:greenbeam 0 255 0
# ores (thx to MilesDyson)
default:stone_with_coal 36 36 36
default:stone_with_copper 115 113 66
default:stone_with_diamond 108 255 255
default:stone_with_gold 255 228 0
default:stone_with_iron 146 73 36
default:stone_with_mese 186 194 0
default:stone_with_tin 202 202 202

View File

@ -1,14 +1,15 @@
body {
height: 100%;
margin: 0;
overflow: hidden;
}
#image-map {
#app {
height: 100%;
}
.full-screen {
width: 100%;
height: 100%;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.leaflet-custom-display {
@ -16,15 +17,6 @@ body {
padding: 5px;
}
#search-menu {
position: absolute;
top: 5%;
bottom: 5%;
left: 5%;
right: 5%;
z-index: 99999;
}
.mapserver-label-icon {
margin-left: -100px !important;
margin-top: -100px !important;

View File

@ -15,8 +15,10 @@
<title>Minetest Mapserver</title>
</head>
<body>
<div id="image-map"></div>
<div id="search-content"></div>
<div id="app">
<br>
<h5 style="text-align: center;">Starting mapserver...</h5>
</div>
<!-- libraries -->
<script src="js/lib/mithril.min.js"></script>
@ -25,9 +27,9 @@
<script src="js/lib/moment.min.js"></script>
<!-- main module -->
<script src="js/main.js" type="module"></script>
<script src="js/main.js?v=1.0" type="module"></script>
<!-- unsupported -->
<script src="js/nomodule.js" nomodule></script>
<!-- no modules -->
<script src="js/bundle.js?v=1.0" nomodule></script>
</body>
</html>

2
static/js/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bundle.js
bundle.js.map

2
static/js/.jshintignore Normal file
View File

@ -0,0 +1,2 @@
bundle.js
lib/*

View File

@ -1,59 +0,0 @@
export default {
setup: function(map, layerMgr){
function updateHash(){
var center = map.getCenter();
window.location.hash =
layerMgr.getCurrentLayer().id + "/" +
center.lng + "/" + center.lat + "/" + map.getZoom();
}
map.on('zoomend', updateHash);
map.on('moveend', updateHash);
map.on('baselayerchange', updateHash);
updateHash();
},
getLayerId: function(){
var hashParts = window.location.hash.substring(1).split("/");
if (hashParts.length == 4){
//new format
return +hashParts[0];
}
return 0;
},
getZoom: function(){
var hashParts = window.location.hash.substring(1).split("/");
if (hashParts.length == 3){
//old format
return +hashParts[2];
} else if (hashParts.length == 4){
//new format
return +hashParts[3];
}
return 11;
},
getCenter: function(){
var hashParts = window.location.hash.substring(1).split("/");
if (hashParts.length == 3){
//old format
return [+hashParts[1], +hashParts[0]];
} else if (hashParts.length == 4){
//new format
return [+hashParts[2], +hashParts[1]];
}
return [0, 0];
}
};

View File

@ -1,46 +1,9 @@
import RealtimeTileLayer from './RealtimeTileLayer.js';
class LayerManager {
setup(wsChannel, layers, map, currentLayerId){
this.listeners = [];
this.currentLayer = layers[0];
setup(layers){
this.layers = layers;
this.map = map;
this.layerObjects = {};
var self = this;
//All layers
layers.forEach(function(layer){
var tileLayer = new RealtimeTileLayer(wsChannel, layer.id, map);
self.layerObjects[layer.name] = tileLayer;
if (layer.id == currentLayerId){
tileLayer.addTo(map);
self.currentLayer = layer;
}
});
map.on('baselayerchange', function (e) {
self.setLayerId(e.layer.layerId);
});
}
switchLayer(layerId){
var self = this;
Object.keys(this.layerObjects).forEach(function(key){
var layerObj = self.layerObjects[key];
if (self.map.hasLayer(layerObj)){
self.map.removeLayer(layerObj);
}
});
Object.keys(this.layerObjects).forEach(function(key){
var layerObj = self.layerObjects[key];
if (layerObj.layerId == layerId){
self.map.addLayer(layerObj);
}
});
this.currentLayer = this.layers[0];
}
setLayerId(layerId){
@ -48,28 +11,20 @@ class LayerManager {
this.layers.forEach(function(layer){
if (layer.id == layerId){
self.currentLayer = layer;
self.listeners.forEach(function(listener){
listener(layer);
});
return;
}
});
if (layerId != this.currentLayer.id){
// layer not found
this.currentLayer = this.layers[0];
}
}
getLayerByY(y){
return this.layers.find(layer => (y >= (layer.from*16) && y <= (layer.to*16)));
}
addListener(listener){
this.listeners.push(listener);
}
removeListener(listener){
this.listeners = this.listeners.filter(function(el){
return el != listener;
});
}
getCurrentLayer(){
return this.currentLayer;
}

View File

@ -1,18 +0,0 @@
import SearchMenu from './search/SearchMenu.js';
import SearchInput from './search/SearchInput.js';
export default L.Control.extend({
initialize: function(wsChannel, opts) {
L.Control.prototype.initialize.call(this, opts);
},
onAdd: function(map) {
var div = L.DomUtil.create('div');
m.mount(div, SearchInput);
m.mount(document.getElementById("search-content"), {
view: () => m(SearchMenu, {map: map})
});
return div;
}
});

33
static/js/compat.js Normal file
View File

@ -0,0 +1,33 @@
export function hashCompat(){
if (window.location.hash) {
const parts = window.location.hash.replace("#", "").split("/");
if (parts.length == 0){
//invalid
return;
}
if (parts[0] == "map"){
//new link
return;
}
if (isNaN(+parts[0])){
//NaN
return;
}
if (parts.length == 3){
// #1799.5/399/10
window.location.hash = `#!/map/0/${parts[2]}/${parts[0]}/${parts[1]}`;
}
if (parts.length == 4) {
// #0/-1799.5/399/10
// #0/5405.875/11148/12
window.location.hash = `#!/map/${parts[0]}/${parts[3]}/${parts[1]}/${parts[2]}`;
}
}
}

View File

@ -0,0 +1,22 @@
import LayerManager from '../LayerManager.js';
function onchange(e){
const params = m.route.param();
params.layerId = e.target.value;
m.route.set("/map/:layerId/:zoom/:lon/:lat", params);
}
export default {
view: function(){
const layers = LayerManager.layers.map(layer => m(
"option",
{ value: layer.id, selected: layer.id == LayerManager.getCurrentLayer().id },
layer.name
));
return m("select", { class: "form-control", onchange: onchange },layers);
}
};

View File

@ -0,0 +1,61 @@
import layerManager from '../LayerManager.js';
import { createMap } from '../map/MapFactory.js';
function setupMap(vnode, id){
const map = createMap(
id,
layerManager.getCurrentLayer().id,
+vnode.attrs.zoom,
+vnode.attrs.lat,
+vnode.attrs.lon
);
vnode.state.map = map;
function updateHash(){
const center = map.getCenter();
const layerId = layerManager.getCurrentLayer().id;
m.route.set(`/map/${layerId}/${map.getZoom()}/` +
`${Math.floor(center.lng)}/${Math.floor(center.lat)}`);
}
map.on('zoomend', updateHash);
map.on('moveend', updateHash);
return map;
}
export default {
oninit(){
this.id = "map_" + Math.floor(Math.random() * 10000);
},
view(){
return m("div", { class: "full-screen", id: this.id });
},
oncreate(vnode){
this.map = setupMap(vnode, this.id);
},
onupdate(vnode){
if (vnode.attrs.layerId != layerManager.getCurrentLayer().id){
//layer changed, recreate map
this.map.remove();
layerManager.setLayerId(vnode.attrs.layerId);
this.map = setupMap(vnode, this.id);
} else {
//position/zoom change
//this.map.setView([+vnode.attrs.lat, +vnode.attrs.lon], +vnode.attrs.zoom);
}
return false;
},
onremove(){
this.map.remove();
}
};

View File

@ -0,0 +1,62 @@
import SearchResult from './SearchResult.js';
import { getMapObjects } from '../api.js';
const state = {
busy: false,
result: []
};
function searchFor(type, key, valuelike){
return getMapObjects({
pos1: { x:-2048, y:-2048, z:-2048 },
pos2: { x:2048, y:2048, z:2048 },
type: type,
attributelike: {
key: key,
value: "%" + valuelike +"%"
}
});
}
function search(query){
state.result = [];
var prom_list = [
searchFor("shop", "out_item", query),
searchFor("poi", "name", query),
searchFor("train", "station", query),
searchFor("travelnet", "station_name", query),
searchFor("bones", "owner", query),
searchFor("locator", "name", query),
searchFor("label", "text", query),
searchFor("digiterm", "display_text", query),
searchFor("digilinelcd", "text", query)
];
Promise.all(prom_list)
.then(function(results){
var arr = [];
results.forEach(function(r) {
arr = arr.concat(r);
});
state.result = arr;
state.busy = false;
});
}
export default {
oncreate(vnode){
search(vnode.attrs.query);
},
view(vnode){
if (state.result.length == 0) {
return m("div", vnode.attrs.query);
} else {
return m(SearchResult, { result: state.result });
}
}
};

View File

@ -1,31 +1,34 @@
import SearchStore from './SearchStore.js';
import SearchService from './SearchService.js';
const state = {
query: ""
};
function doSearch(){
m.route.set(`/search/${state.query}`);
}
export default {
view: function(){
function handleInput(e){
SearchStore.query = e.target.value;
state.query = e.target.value;
}
function handleKeyDown(e){
if (e.keyCode == 13){
SearchService.search();
doSearch();
}
}
function handleDoSearch(){
SearchService.search();
}
return m("div", { class: "input-group mb-3" }, [
m("input[type=text]", {
placeholder: "Search",
class: "form-control",
oninput: handleInput,
onkeydown: handleKeyDown,
value: SearchStore.query
value: state.query
}),
m("div", { class: "input-group-append", onclick: handleDoSearch }, [
m("div", { class: "input-group-append", onclick: doSearch }, [
m("span", { class: "input-group-text" }, [
m("i", { class: "fa fa-search"})
])

View File

@ -1,9 +1,8 @@
import SearchStore from './SearchStore.js';
import layerMgr from '../LayerManager.js';
export default {
view: function(vnode){
var map = vnode.attrs.map;
var result = vnode.attrs.result;
function getLayer(obj){
var layer = layerMgr.getLayerByY(obj.y);
@ -16,7 +15,7 @@ export default {
return m("span", {class:"badge badge-success"}, text);
}
var rows = SearchStore.result.map(function(obj){
var rows = result.map(function(obj){
var row_classes = "";
var description = obj.type;
@ -100,10 +99,18 @@ export default {
description = m("span", [
"Shop, trading ",
m("span", {class:"badge badge-primary"}, obj.attributes.out_count + "x"),
m("span", {class:"badge badge-primary"},
obj.attributes.out_count,
"x",
m("i", {class:"fa fa-cart-arrow-down"})
),
m("span", {class:"badge badge-info"}, obj.attributes.out_item),
" for ",
m("span", {class:"badge badge-primary"}, obj.attributes.in_count + "x"),
m("span", {class:"badge badge-primary"},
obj.attributes.in_count,
"x",
m("i", {class:"fa fa-money-bill"})
),
m("span", {class:"badge badge-info"}, obj.attributes.in_item),
" Stock: ",
m("span", {class:"badge badge-info"}, obj.attributes.stock)
@ -112,11 +119,7 @@ export default {
function onclick(){
var layer = layerMgr.getLayerByY(obj.y);
layerMgr.switchLayer(layer.id);
map.setView([obj.z, obj.x], 12);
SearchStore.show = false;
m.route.set(`/map/${layer.id}/${12}/${obj.x}/${obj.z}`);
}
return m("tr", {"class": row_classes}, [

12
static/js/config.js Normal file
View File

@ -0,0 +1,12 @@
var config;
export default {
get(){
return config;
},
set(cfg){
config = cfg;
}
};

View File

@ -1,5 +1,21 @@
import { getConfig } from './api.js';
import { setup } from './map.js';
import routes from './routes.js';
import wsChannel from './WebSocketChannel.js';
import config from './config.js';
import { hashCompat } from './compat.js';
import layerManager from './LayerManager.js';
getConfig().then(setup);
// hash route compat
hashCompat();
getConfig()
.then(cfg => {
layerManager.setup(cfg.layers);
config.set(cfg);
wsChannel.connect();
m.route(document.getElementById("app"), "/map/0/12/0/0", routes);
})
.catch(e => {
document.getElementById("app").innerHTML = e;
});

View File

@ -1,42 +0,0 @@
import wsChannel from './WebSocketChannel.js';
import Hashroute from './Hashroute.js';
import SimpleCRS from './SimpleCRS.js';
import CoordinatesDisplay from './CoordinatesDisplay.js';
import WorldInfoDisplay from './WorldInfoDisplay.js';
import SearchControl from './SearchControl.js';
import Overlaysetup from './Overlaysetup.js';
import layerManager from './LayerManager.js';
export function setup(cfg){
wsChannel.connect();
var map = L.map('image-map', {
minZoom: 2,
maxZoom: 12,
center: Hashroute.getCenter(),
zoom: Hashroute.getZoom(),
crs: SimpleCRS
});
map.attributionControl.addAttribution('<a href="https://github.com/minetest-tools/mapserver">Minetest Mapserver</a>');
var overlays = {};
layerManager.setup(wsChannel, cfg.layers, map, Hashroute.getLayerId());
//All overlays
Overlaysetup(cfg, map, overlays, wsChannel, layerManager);
new CoordinatesDisplay({ position: 'bottomleft' }).addTo(map);
new WorldInfoDisplay(wsChannel, { position: 'bottomright' }).addTo(map);
if (cfg.enablesearch){
new SearchControl(wsChannel, { position: 'topright' }).addTo(map);
}
//layer control
L.control.layers(layerManager.layerObjects, overlays, { position: "topright" }).addTo(map);
Hashroute.setup(map, layerManager);
}

View File

@ -0,0 +1,48 @@
var customOverlays = {};
try {
customOverlays = JSON.parse(localStorage["mapserver-customOverlays"]);
} catch (e){}
function save(){
localStorage["mapserver-customOverlays"] = JSON.stringify(customOverlays);
}
function onAddLayer(e){
customOverlays[e.name] = true;
save();
}
function onRemoveLayer(e){
customOverlays[e.name] = false;
save();
}
export default function(map, overlays){
Object.keys(customOverlays)
.filter(name => overlays[name])
.forEach(name => {
const layer = overlays[name];
if (customOverlays[name] && !map.hasLayer(layer)){
//Add
map.addLayer(layer);
}
if (!customOverlays[name] && map.hasLayer(layer)){
//Remove
map.removeLayer(layer);
}
});
map.on('unload', () => {
map.off('overlayadd', onAddLayer);
map.off('overlayremove', onRemoveLayer);
});
map.on('overlayadd', onAddLayer);
map.on('overlayremove', onRemoveLayer);
}

View File

@ -0,0 +1,43 @@
import wsChannel from '../WebSocketChannel.js';
import SimpleCRS from './SimpleCRS.js';
import CoordinatesDisplay from './CoordinatesDisplay.js';
import WorldInfoDisplay from './WorldInfoDisplay.js';
import TopRightControl from './TopRightControl.js';
import Overlaysetup from './Overlaysetup.js';
import CustomOverlay from './CustomOverlay.js';
import RealtimeTileLayer from './RealtimeTileLayer.js';
import config from '../config.js';
export function createMap(node, layerId, zoom, lat, lon){
const cfg = config.get();
const map = L.map(node, {
minZoom: 2,
maxZoom: 12,
center: [lat, lon],
zoom: zoom,
crs: SimpleCRS
});
map.attributionControl.addAttribution('<a href="https://github.com/minetest-mapserver/mapserver">Minetest Mapserver</a>');
var tileLayer = new RealtimeTileLayer(wsChannel, layerId, map);
tileLayer.addTo(map);
//All overlays
var overlays = {};
Overlaysetup(cfg, map, overlays);
CustomOverlay(map, overlays);
new CoordinatesDisplay({ position: 'bottomleft' }).addTo(map);
new WorldInfoDisplay(wsChannel, { position: 'bottomright' }).addTo(map);
new TopRightControl({ position: 'topright' }).addTo(map);
//layer control
L.control.layers({}, overlays, { position: "topright" }).addTo(map);
return map;
}

View File

@ -22,140 +22,140 @@ import BorderOverlay from './overlays/BorderOverlay.js';
import TrainOverlay from './overlays/TrainOverlay.js';
import TrainsignalOverlay from './overlays/TrainsignalOverlay.js';
export default function(cfg, map, overlays, wsChannel, layerMgr){
export default function(cfg, map, overlays, wsChannel){
function isDefault(key){
return cfg.defaultoverlays.indexOf(key) >= 0;
}
if (cfg.mapobjects.mapserver_player) {
overlays.Player = new PlayerOverlay(wsChannel, layerMgr);
overlays.Player = new PlayerOverlay();
if (isDefault("mapserver_player")) {
map.addLayer(overlays.Player);
}
}
if (cfg.mapobjects.mapserver_poi) {
overlays.POI = new PoiOverlay(wsChannel, layerMgr);
overlays.POI = new PoiOverlay(wsChannel);
if (isDefault("mapserver_poi")) {
map.addLayer(overlays.POI);
}
}
if (cfg.mapobjects.smartshop || cfg.mapobjects.fancyvend) {
overlays.Shop = new ShopOverlay(wsChannel, layerMgr);
overlays.Shop = new ShopOverlay();
if (isDefault("smartshop") || isDefault("fancyvend")) {
map.addLayer(overlays.Shop);
}
}
if (cfg.mapobjects.mapserver_label) {
overlays.Label = new LabelOverlay(wsChannel, layerMgr);
overlays.Label = new LabelOverlay();
if (isDefault("mapserver_label")) {
map.addLayer(overlays.Label);
}
}
if (cfg.mapobjects.mapserver_trainline) {
overlays.Trainlines = new TrainlineOverlay(wsChannel, layerMgr);
overlays.Trainlines = new TrainlineOverlay();
if (isDefault("mapserver_trainline")) {
map.addLayer(overlays.Trainlines);
}
}
if (cfg.mapobjects.mapserver_border) {
overlays.Border = new BorderOverlay(wsChannel, layerMgr);
overlays.Border = new BorderOverlay();
if (isDefault("mapserver_border")) {
map.addLayer(overlays.Border);
}
}
if (cfg.mapobjects.travelnet) {
overlays.Travelnet = new TravelnetOverlay(wsChannel, layerMgr);
overlays.Travelnet = new TravelnetOverlay();
if (isDefault("travelnet")) {
map.addLayer(overlays.Travelnet);
}
}
if (cfg.mapobjects.bones) {
overlays.Bones = new BonesOverlay(wsChannel, layerMgr);
overlays.Bones = new BonesOverlay();
if (isDefault("bones")) {
map.addLayer(overlays.Bones);
}
}
if (cfg.mapobjects.digilines) {
overlays["Digilines LCD"] = new LcdOverlay(wsChannel, layerMgr);
overlays["Digilines LCD"] = new LcdOverlay();
if (isDefault("digilines")) {
map.addLayer(overlays["Digilines LCD"]);
}
}
if (cfg.mapobjects.digiterms) {
overlays.Digiterms = new DigitermOverlay(wsChannel, layerMgr);
overlays.Digiterms = new DigitermOverlay();
if (isDefault("digiterms")) {
map.addLayer(overlays.Digiterms);
}
}
if (cfg.mapobjects.luacontroller) {
overlays["Lua Controller"] = new LuacontrollerOverlay(wsChannel, layerMgr);
overlays["Lua Controller"] = new LuacontrollerOverlay();
if (isDefault("luacontroller")) {
map.addLayer(overlays["Lua Controller"]);
}
}
if (cfg.mapobjects.technic_anchor) {
overlays["Technic Anchor"] = new TechnicAnchorOverlay(wsChannel, layerMgr);
overlays["Technic Anchor"] = new TechnicAnchorOverlay();
if (isDefault("technic_anchor")) {
map.addLayer(overlays["Technic Anchor"]);
}
}
if (cfg.mapobjects.technic_quarry) {
overlays["Technic Quarry"] = new TechnicQuarryOverlay(wsChannel, layerMgr);
overlays["Technic Quarry"] = new TechnicQuarryOverlay();
if (isDefault("technic_quarry")) {
map.addLayer(overlays["Technic Quarry"]);
}
}
if (cfg.mapobjects.technic_switch) {
overlays["Technic Switching station"] = new TechnicSwitchOverlay(wsChannel, layerMgr);
overlays["Technic Switching station"] = new TechnicSwitchOverlay();
if (isDefault("technic_switch")) {
map.addLayer(overlays["Technic Switching station"]);
}
}
if (cfg.mapobjects.protector) {
overlays.Protector = new ProtectorOverlay(wsChannel, layerMgr);
overlays.Protector = new ProtectorOverlay();
if (isDefault("protector")) {
map.addLayer(overlays.Protector);
}
}
if (cfg.mapobjects.xpprotector) {
overlays["XP Protector"] = new XPProtectorOverlay(wsChannel, layerMgr);
overlays["XP Protector"] = new XPProtectorOverlay();
if (isDefault("xpprotector")) {
map.addLayer(overlays["XP Protector"]);
}
}
if (cfg.mapobjects.privprotector) {
overlays["Priv Protector"] = new PrivProtectorOverlay(wsChannel, layerMgr);
overlays["Priv Protector"] = new PrivProtectorOverlay();
if (isDefault("privprotector")) {
map.addLayer(overlays["Priv Protector"]);
}
}
if (cfg.mapobjects.mission) {
overlays.Missions = new MissionOverlay(wsChannel, layerMgr);
overlays.Missions = new MissionOverlay();
if (isDefault("mission")) {
map.addLayer(overlays.Missions);
}
}
if (cfg.mapobjects.train) {
overlays.Trains = new TrainOverlay(wsChannel, layerMgr);
overlays.Trains = new TrainOverlay();
if (isDefault("train")) {
map.addLayer(overlays.Trains);
@ -163,7 +163,7 @@ export default function(cfg, map, overlays, wsChannel, layerMgr){
}
if (cfg.mapobjects.trainsignal) {
overlays.Trainsignals = new TrainsignalOverlay(wsChannel, layerMgr);
overlays.Trainsignals = new TrainsignalOverlay();
if (isDefault("trainsignal")) {
map.addLayer(overlays.Trainsignals);
@ -171,21 +171,21 @@ export default function(cfg, map, overlays, wsChannel, layerMgr){
}
if (cfg.mapobjects.minecart) {
overlays.Minecart = new MinecartOverlay(wsChannel, layerMgr);
overlays.Minecart = new MinecartOverlay();
if (isDefault("minecart")) {
map.addLayer(overlays.Minecart);
}
}
if (cfg.mapobjects.atm) {
overlays.ATM = new ATMOverlay(wsChannel, layerMgr);
overlays.ATM = new ATMOverlay();
if (isDefault("atm")) {
map.addLayer(overlays.ATM);
}
}
if (cfg.mapobjects.locator) {
overlays.Locator = new LocatorOverlay(wsChannel, layerMgr);
overlays.Locator = new LocatorOverlay();
if (isDefault("locator")) {
map.addLayer(overlays.Locator);
}

View File

@ -0,0 +1,27 @@
import SearchInput from '../components/SearchInput.js';
import LayerSelector from '../components/LayerSelector.js';
import config from '../config.js';
const Component = {
view: function(){
const cfg = config.get();
return m("div", [
cfg.enablesearch ? m(SearchInput) : null,
m(LayerSelector)
]);
}
};
export default L.Control.extend({
initialize: function(wsChannel, opts) {
L.Control.prototype.initialize.call(this, opts);
},
onAdd: function() {
var div = L.DomUtil.create('div');
m.mount(div, Component);
return div;
}
});

View File

@ -1,8 +1,8 @@
import AbstractIconOverlay from './AbstractIconOverlay.js';
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "atm");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "atm");
},
getMaxDisplayedZoom: function(){

View File

@ -1,21 +1,16 @@
import debounce from '../util/debounce.js';
import { getMapObjects } from '../api.js';
import debounce from '../../util/debounce.js';
import layerMgr from '../../LayerManager.js';
import { getMapObjects } from '../../api.js';
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr, type) {
initialize: function(type) {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.type = type;
this.onLayerChange = this.onLayerChange.bind(this);
this.onMapMove = debounce(this.onMapMove.bind(this), 50);
},
onLayerChange: function(){
this.reDraw();
},
onMapMove: function(){
this.reDraw();
@ -61,7 +56,7 @@ export default L.LayerGroup.extend({
return;
}
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
var min = this._map.getBounds().getSouthWest();
var max = this._map.getBounds().getNorthEast();
@ -90,7 +85,6 @@ export default L.LayerGroup.extend({
this.map = map;
map.on("zoomend", this.onMapMove);
map.on("moveend", this.onMapMove);
this.layerMgr.addListener(this.onLayerChange);
this.reDraw(true);
},
@ -98,7 +92,6 @@ export default L.LayerGroup.extend({
this.clearLayers();
map.off("zoomend", this.onMapMove);
map.off("moveend", this.onMapMove);
this.layerMgr.removeListener(this.onLayerChange);
}
});

View File

@ -1,12 +1,13 @@
import debounce from '../util/debounce.js';
import { getMapObjects } from '../api.js';
import debounce from '../../util/debounce.js';
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
import { getMapObjects } from '../../api.js';
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr, type, icon) {
initialize: function(type, icon) {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.type = type;
this.icon = icon;
@ -66,7 +67,7 @@ export default L.LayerGroup.extend({
this.currentObjects = {};
}
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
var min = this.map.getBounds().getSouthWest();
var max = this.map.getBounds().getNorthEast();
@ -134,8 +135,7 @@ export default L.LayerGroup.extend({
this.map = map;
map.on("zoomend", this.onMapMove);
map.on("moveend", this.onMapMove);
this.layerMgr.addListener(this.onLayerChange);
this.wsChannel.addListener("mapobject-created", this.onMapObjectUpdated);
wsChannel.addListener("mapobject-created", this.onMapObjectUpdated);
this.reDraw(true);
},
@ -143,8 +143,7 @@ export default L.LayerGroup.extend({
this.clearLayers();
map.off("zoomend", this.onMapMove);
map.off("moveend", this.onMapMove);
this.layerMgr.removeListener(this.onLayerChange);
this.wsChannel.removeListener("mapobject-created", this.onMapObjectUpdated);
wsChannel.removeListener("mapobject-created", this.onMapObjectUpdated);
}
});

View File

@ -9,8 +9,8 @@ var BonesIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "bones", BonesIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "bones", BonesIcon);
},
createPopup: function(bones){

View File

@ -1,8 +1,8 @@
import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';
export default AbstractGeoJsonOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractGeoJsonOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "border");
initialize: function() {
AbstractGeoJsonOverlay.prototype.initialize.call(this, "border");
},
getMaxDisplayedZoom: function(){

View File

@ -9,8 +9,8 @@ var DigitermIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "digiterm", DigitermIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "digiterm", DigitermIcon);
},
createPopup: function(lcd){

View File

@ -1,8 +1,8 @@
import AbstractIconOverlay from './AbstractIconOverlay.js';
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "label");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "label");
},
getMaxDisplayedZoom: function(){
@ -21,7 +21,7 @@ export default AbstractIconOverlay.extend({
var notVisible = (fontSize < 2 || fontSize > 50);
const html = `
<svg height='${height}' width='${width}' text-anchor='middle'>
<svg height='${height}' width='${width}' text-anchor='middle' style='pointer-events: none;'>
<text x='${width/2}' y='${height/2}'
font-size='${fontSize}px'
fill='${lbl.attributes.color}'

View File

@ -9,8 +9,8 @@ var LcdIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "digilinelcd", LcdIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "digilinelcd", LcdIcon);
},
createPopup: function(lcd){

View File

@ -1,8 +1,8 @@
import AbstractIconOverlay from './AbstractIconOverlay.js';
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "locator");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "locator");
},
getMaxDisplayedZoom: function(){

View File

@ -17,8 +17,8 @@ var LuacontrollerBurntIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "luacontroller");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "luacontroller");
},
getIcon: function(ctrl){

View File

@ -1,21 +1,22 @@
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
let minecarts = [];
//update minecarts all the time
wsChannel.addListener("minetest-info", function(info){
minecarts = info.minecarts || [];
});
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr) {
initialize: function() {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.currentObjects = {}; // name => marker
this.minecarts = [];
this.reDraw = this.reDraw.bind(this);
this.onMinetestUpdate = this.onMinetestUpdate.bind(this);
//update players all the time
this.wsChannel.addListener("minetest-info", function(info){
this.minecarts = info.minecarts || [];
}.bind(this));
},
createMarker: function(cart){
@ -38,7 +39,7 @@ export default L.LayerGroup.extend({
},
isCartInCurrentLayer: function(cart){
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
return (cart.pos.y >= (mapLayer.from*16) && cart.pos.y <= (mapLayer.to*16));
},
@ -47,7 +48,7 @@ export default L.LayerGroup.extend({
onMinetestUpdate: function(/*info*/){
var self = this;
this.minecarts.forEach(function(cart){
minecarts.forEach(function(cart){
var isInLayer = self.isCartInCurrentLayer(cart);
if (!isInLayer){
@ -76,7 +77,7 @@ export default L.LayerGroup.extend({
});
Object.keys(self.currentObjects).forEach(function(existingId){
var cartIsActive = self.minecarts.find(function(t){
var cartIsActive = minecarts.find(function(t){
return t.id == existingId;
});
@ -92,9 +93,7 @@ export default L.LayerGroup.extend({
this.currentObjects = {};
this.clearLayers();
var mapLayer = this.layerMgr.getCurrentLayer();
this.minecarts.forEach(function(cart){
minecarts.forEach(function(cart){
if (!self.isCartInCurrentLayer(cart)){
//not in current layer
return;
@ -108,14 +107,12 @@ export default L.LayerGroup.extend({
},
onAdd: function(/*map*/) {
this.layerMgr.addListener(this.reDraw);
this.wsChannel.addListener("minetest-info", this.onMinetestUpdate);
wsChannel.addListener("minetest-info", this.onMinetestUpdate);
this.reDraw();
},
onRemove: function(/*map*/) {
this.clearLayers();
this.layerMgr.removeListener(this.reDraw);
this.wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
}
});

View File

@ -9,8 +9,8 @@ var MissionIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "mission", MissionIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "mission", MissionIcon);
},
createPopup: function(mission){

View File

@ -1,3 +1,12 @@
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
let players = [];
//update players all the time
wsChannel.addListener("minetest-info", function(info){
players = info.players || [];
});
var PlayerIcon = L.icon({
iconUrl: 'pics/sam.png',
@ -8,22 +17,13 @@ var PlayerIcon = L.icon({
});
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr) {
initialize: function() {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.currentObjects = {}; // name => marker
this.players = [];
this.reDraw = this.reDraw.bind(this);
this.onMinetestUpdate = this.onMinetestUpdate.bind(this);
//update players all the time
this.wsChannel.addListener("minetest-info", function(info){
this.players = info.players || [];
}.bind(this));
},
createPopup: function(player){
@ -62,14 +62,17 @@ export default L.LayerGroup.extend({
},
isPlayerInCurrentLayer: function(player){
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
return (player.pos.y >= (mapLayer.from*16) && player.pos.y <= (mapLayer.to*16));
return (
player.pos.y >= (mapLayer.from*16) &&
player.pos.y <= ((mapLayer.to*16) + 15)
);
},
onMinetestUpdate: function(/*info*/){
this.players.forEach(player => {
players.forEach(player => {
var isInLayer = this.isPlayerInCurrentLayer(player);
if (!isInLayer){
@ -99,7 +102,7 @@ export default L.LayerGroup.extend({
});
Object.keys(this.currentObjects).forEach(existingName => {
var playerIsActive = this.players.find(function(p){
var playerIsActive = players.find(function(p){
return p.name == existingName;
});
@ -115,9 +118,7 @@ export default L.LayerGroup.extend({
this.currentObjects = {};
this.clearLayers();
var mapLayer = this.layerMgr.getCurrentLayer();
this.players.forEach(player => {
players.forEach(player => {
if (!this.isPlayerInCurrentLayer(player)){
//not in current layer
return;
@ -131,14 +132,12 @@ export default L.LayerGroup.extend({
},
onAdd: function(/*map*/) {
this.layerMgr.addListener(this.reDraw);
this.wsChannel.addListener("minetest-info", this.onMinetestUpdate);
wsChannel.addListener("minetest-info", this.onMinetestUpdate);
this.reDraw();
},
onRemove: function(/*map*/) {
this.clearLayers();
this.layerMgr.removeListener(this.reDraw);
this.wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
}
});

View File

@ -1,8 +1,8 @@
import AbstractIconOverlay from './AbstractIconOverlay.js';
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "poi");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "poi");
},
getIcon: function(obj){

View File

@ -1,8 +1,8 @@
import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';
export default AbstractGeoJsonOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractGeoJsonOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "privprotector");
initialize: function() {
AbstractGeoJsonOverlay.prototype.initialize.call(this, "privprotector");
},
createFeature: function(protector){

View File

@ -1,8 +1,8 @@
import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';
export default AbstractGeoJsonOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractGeoJsonOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "protector");
initialize: function() {
AbstractGeoJsonOverlay.prototype.initialize.call(this, "protector");
},
getMaxDisplayedZoom: function(){

View File

@ -16,8 +16,8 @@ var ShopEmptyIcon = L.icon({
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "shop");
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "shop");
},
getMaxDisplayedZoom: function(){

View File

@ -9,8 +9,8 @@ var TechnicAnchorIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "technicanchor", TechnicAnchorIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "technicanchor", TechnicAnchorIcon);
},
createPopup: function(lcd){

View File

@ -9,8 +9,8 @@ var TechnicQuarryIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "technicquarry", TechnicQuarryIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "technicquarry", TechnicQuarryIcon);
},
createPopup: function(quarry){

View File

@ -9,8 +9,8 @@ var TechnicSwitchIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "technicswitch", TechnicSwitchIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "technicswitch", TechnicSwitchIcon);
},
createPopup: function(sw){

View File

@ -1,3 +1,5 @@
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
function getTrainImageUrlForType(type){
switch(type){
@ -29,23 +31,20 @@ function getTrainImageUrlForType(type){
}
}
let trains = [];
//update trains all the time
wsChannel.addListener("minetest-info", function(info){
trains = info.trains || [];
});
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr) {
initialize: function() {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.currentObjects = {}; // name => marker
this.trains = [];
this.reDraw = this.reDraw.bind(this);
this.onMinetestUpdate = this.onMinetestUpdate.bind(this);
//update players all the time
this.wsChannel.addListener("minetest-info", function(info){
this.trains = info.trains || [];
}.bind(this));
},
createPopup: function(train){
@ -68,7 +67,7 @@ export default L.LayerGroup.extend({
getMaxDisplayedZoom: function(){
return 8;
return 10;
},
createMarker: function(train){
@ -100,7 +99,7 @@ export default L.LayerGroup.extend({
},
isTrainInCurrentLayer: function(train){
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
return (train.pos.y >= (mapLayer.from*16) && train.pos.y <= (mapLayer.to*16));
},
@ -114,7 +113,7 @@ export default L.LayerGroup.extend({
return;
}
this.trains.forEach(train => {
trains.forEach(train => {
var isInLayer = this.isTrainInCurrentLayer(train);
if (!isInLayer){
@ -144,7 +143,7 @@ export default L.LayerGroup.extend({
});
Object.keys(this.currentObjects).forEach(existingId => {
var trainIsActive = this.trains.find(function(t){
var trainIsActive = trains.find(function(t){
return t.id == existingId;
});
@ -164,9 +163,9 @@ export default L.LayerGroup.extend({
return;
}
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
this.trains.forEach(train => {
trains.forEach(train => {
if (!this.isTrainInCurrentLayer(train)){
//not in current layer
return;
@ -181,14 +180,12 @@ export default L.LayerGroup.extend({
onAdd: function(map) {
this.map = map;
this.layerMgr.addListener(() => this.reDraw());
this.wsChannel.addListener("minetest-info", () => this.onMinetestUpdate());
wsChannel.addListener("minetest-info", this.onMinetestUpdate);
this.reDraw();
},
onRemove: function(/*map*/) {
this.clearLayers();
this.layerMgr.removeListener(() => this.reDraw());
this.wsChannel.removeListener("minetest-info", () => this.onMinetestUpdate());
wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
}
});

View File

@ -1,8 +1,8 @@
import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';
export default AbstractGeoJsonOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractGeoJsonOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "train");
initialize: function() {
AbstractGeoJsonOverlay.prototype.initialize.call(this, "train");
},
createGeoJson: function(objects){

View File

@ -1,3 +1,5 @@
import wsChannel from '../../WebSocketChannel.js';
import layerMgr from '../../LayerManager.js';
var IconOn = L.icon({
iconUrl: "pics/advtrains/advtrains_signal_on.png",
@ -13,25 +15,19 @@ var IconOff = L.icon({
popupAnchor: [0, -16]
});
let signals = [];
//update signals all the time
wsChannel.addListener("minetest-info", function(info){
signals = info.signals || [];
});
export default L.LayerGroup.extend({
initialize: function(wsChannel, layerMgr) {
initialize: function() {
L.LayerGroup.prototype.initialize.call(this);
this.layerMgr = layerMgr;
this.wsChannel = wsChannel;
this.currentObjects = {}; // name => marker
this.signals = [];
this.reDraw = this.reDraw.bind(this);
this.onMinetestUpdate = this.onMinetestUpdate.bind(this);
//update players all the time
this.wsChannel.addListener("minetest-info", function(info){
this.signals = info.signals || [];
}.bind(this));
},
createPopup: function(signal){
@ -61,7 +57,7 @@ export default L.LayerGroup.extend({
},
isSignalInCurrentLayer: function(signal){
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
return (signal.pos.y >= (mapLayer.from*16) && signal.pos.y <= (mapLayer.to*16));
},
@ -75,7 +71,7 @@ export default L.LayerGroup.extend({
return;
}
this.signals.forEach(signal => {
signals.forEach(signal => {
var isInLayer = this.isSignalInCurrentLayer(signal);
var signalId = this.hashPos(signal.pos.x, signal.pos.y, signal.pos.z);
@ -107,7 +103,7 @@ export default L.LayerGroup.extend({
});
Object.keys(this.currentObjects).forEach(existingId => {
var signalIsActive = this.signals.find((t) => {
var signalIsActive = signals.find((t) => {
var hash = this.hashPos(t.pos.x, t.pos.y, t.pos.z);
return hash == existingId;
});
@ -127,9 +123,9 @@ export default L.LayerGroup.extend({
return;
}
var mapLayer = this.layerMgr.getCurrentLayer();
var mapLayer = layerMgr.getCurrentLayer();
this.signals.forEach(signal => {
signals.forEach(signal => {
if (!this.isSignalInCurrentLayer(signal)){
//not in current layer
return;
@ -145,14 +141,12 @@ export default L.LayerGroup.extend({
onAdd: function(map) {
this.map = map;
this.layerMgr.addListener(() => this.reDraw());
this.wsChannel.addListener("minetest-info", () => this.onMinetestUpdate());
wsChannel.addListener("minetest-info", this.onMinetestUpdate);
this.reDraw();
},
onRemove: function(/*map*/) {
this.clearLayers();
this.layerMgr.removeListener(() => this.reDraw());
this.wsChannel.removeListener("minetest-info", () => this.onMinetestUpdate());
wsChannel.removeListener("minetest-info", this.onMinetestUpdate);
}
});

View File

@ -9,8 +9,8 @@ var TravelnetIcon = L.icon({
});
export default AbstractIconOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractIconOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "travelnet", TravelnetIcon);
initialize: function() {
AbstractIconOverlay.prototype.initialize.call(this, "travelnet", TravelnetIcon);
},
createPopup: function(travelnet){

View File

@ -1,8 +1,8 @@
import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js';
export default AbstractGeoJsonOverlay.extend({
initialize: function(wsChannel, layerMgr) {
AbstractGeoJsonOverlay.prototype.initialize.call(this, wsChannel, layerMgr, "xpprotector");
initialize: function() {
AbstractGeoJsonOverlay.prototype.initialize.call(this, "xpprotector");
},
createFeature: function(protector){

View File

@ -1,5 +1,5 @@
m.mount(document.getElementById("image-map"), {
m.mount(document.getElementById("app"), {
view: function(){
return m("div", "I'm sorry, your browser is just too old ;)");
}

View File

@ -0,0 +1,10 @@
export default {
input: 'main.js',
output: {
file :'bundle.js',
format: 'umd',
sourcemap: true,
compact: true
}
};

8
static/js/routes.js Normal file
View File

@ -0,0 +1,8 @@
import Map from './components/Map.js';
import Search from './components/Search.js';
export default {
"/map/:layerId/:zoom/:lon/:lat": Map,
"/search/:query": Search
};

View File

@ -1,36 +0,0 @@
import SearchService from './SearchService.js';
import SearchStore from './SearchStore.js';
import SearchResult from './SearchResult.js';
export default {
view: function(vnode){
var style = {};
if (!SearchStore.show) {
style.display = "none";
}
function close(){
SearchService.clear();
}
function getContent(){
if (SearchStore.busy){
return m("div", m("i", { class: "fa fa-spinner"}));
} else {
return m(SearchResult, { map: vnode.attrs.map });
}
}
return m("div", { class: "card", id: "search-menu", style: style }, [
m("div", { class: "card-header" }, [
m("i", { class: "fa fa-search"}),
"Search",
m("i", { class: "fa fa-times float-right", onclick: close }),
]),
m("div", { class: "card-body", style: {overflow: "auto"} }, getContent())
]);
}
};

View File

@ -1,62 +0,0 @@
import SearchStore from './SearchStore.js';
import { getMapObjects } from '../api.js';
export default {
search: function(){
SearchStore.show = true;
this.fetchData();
},
fetchData: function(){
SearchStore.result = [];
if (!SearchStore.query){
return;
}
SearchStore.busy = true;
function searchFor(type, key, valuelike){
return getMapObjects({
pos1: { x:-2048, y:-2048, z:-2048 },
pos2: { x:2048, y:2048, z:2048 },
type: type,
attributelike: {
key: key,
value: "%" + valuelike +"%"
}
});
}
var prom_list = [
searchFor("shop", "out_item", SearchStore.query),
searchFor("poi", "name", SearchStore.query),
searchFor("train", "station", SearchStore.query),
searchFor("travelnet", "station_name", SearchStore.query),
searchFor("bones", "owner", SearchStore.query),
searchFor("locator", "name", SearchStore.query),
searchFor("label", "text", SearchStore.query),
searchFor("digiterm", "display_text", SearchStore.query),
searchFor("digilinelcd", "text", SearchStore.query)
];
Promise.all(prom_list)
.then(function(results){
var arr = [];
results.forEach(function(r) {
arr = arr.concat(r);
});
SearchStore.result = arr;
SearchStore.busy = false;
});
},
clear: function(){
SearchStore.result = [];
SearchStore.show = false;
}
};

View File

@ -1,7 +0,0 @@
export default {
query: "",
show: false,
busy: false,
result: []
};

View File

@ -3,6 +3,8 @@ package tilerenderer
import (
"bytes"
"errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/sirupsen/logrus"
"image"
"image/draw"
"image/png"
@ -14,11 +16,6 @@ import (
"mapserver/tiledb"
"strconv"
"time"
"github.com/disintegration/imaging"
"github.com/sirupsen/logrus"
"github.com/prometheus/client_golang/prometheus"
)
type TileRenderer struct {
@ -29,6 +26,33 @@ type TileRenderer struct {
Eventbus *eventbus.Eventbus
}
func resizeImage(src *image.NRGBA, tgt *image.NRGBA, xoffset int, yoffset int) {
if src == nil {
return
}
w := src.Bounds().Dy() >> 1
h := src.Bounds().Dx() >> 1
sinc := src.Bounds().Dy() * 4
tinc := tgt.Bounds().Dx() * 4
for y := 0; y < h; y++ {
six := y * sinc * 2
tix := 4*xoffset + (yoffset+y)*tinc
for x := 0; x < w; x++ {
r := (uint16(src.Pix[six]) + uint16(src.Pix[six+4]) + uint16(src.Pix[six+sinc]) + uint16(src.Pix[six+sinc+4])) >> 2
g := (uint16(src.Pix[six+1]) + uint16(src.Pix[six+5]) + uint16(src.Pix[six+sinc+1]) + uint16(src.Pix[six+sinc+5])) >> 2
b := (uint16(src.Pix[six+2]) + uint16(src.Pix[six+6]) + uint16(src.Pix[six+sinc+2]) + uint16(src.Pix[six+sinc+6])) >> 2
a := (uint16(src.Pix[six+3]) + uint16(src.Pix[six+7]) + uint16(src.Pix[six+sinc+3]) + uint16(src.Pix[six+sinc+7])) >> 2
tgt.Pix[tix] = uint8(r)
tgt.Pix[tix+1] = uint8(g)
tgt.Pix[tix+2] = uint8(b)
tgt.Pix[tix+3] = uint8(a)
tix += 4
six += 8
}
}
}
func NewTileRenderer(mapblockrenderer *mapblockrenderer.MapBlockRenderer,
tdb *tiledb.TileDB,
dba db.DBAccessor,
@ -44,38 +68,34 @@ func NewTileRenderer(mapblockrenderer *mapblockrenderer.MapBlockRenderer,
}
const (
IMG_SIZE = 256
IMG_SIZE = 256
SUB_IMG_SIZE = IMG_SIZE >> 1
)
func (tr *TileRenderer) Render(tc *coords.TileCoords) ([]byte, error) {
func (tr *TileRenderer) Render(tc *coords.TileCoords) error {
//No tile in db
img, data, err := tr.renderImage(tc, 2)
_, err := tr.renderImage(tc, 2)
if err != nil {
return nil, err
return err
}
if img == nil {
//empty tile
return nil, nil
}
return data, nil
return nil
}
func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (*image.NRGBA, []byte, error) {
func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (*image.NRGBA, error) {
if recursionDepth < 2 {
cachedtile, err := tr.tdb.GetTile(tc)
if err != nil {
return nil, nil, err
return nil, err
}
if cachedtile != nil {
reader := bytes.NewReader(cachedtile)
cachedimg, err := png.Decode(reader)
if err != nil {
return nil, nil, err
return nil, err
}
rect := image.Rectangle{
@ -87,14 +107,14 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
draw.Draw(img, rect, cachedimg, image.ZP, draw.Src)
log.WithFields(logrus.Fields{"x": tc.X, "y": tc.Y, "zoom": tc.Zoom}).Debug("Cached image")
return img, cachedtile, nil
return img, nil
}
}
if recursionDepth <= 1 && tc.Zoom < 13 {
//non-cached layer and not in "origin" zoom, skip tile
log.WithFields(logrus.Fields{"x": tc.X, "y": tc.Y, "zoom": tc.Zoom}).Debug("Skip image")
return nil, nil, nil
return nil, nil
}
log.WithFields(logrus.Fields{"x": tc.X, "y": tc.Y, "zoom": tc.Zoom}).Debug("RenderImage")
@ -106,11 +126,11 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
currentLayer := layer.FindLayerById(tr.layers, tc.LayerId)
if currentLayer == nil {
return nil, nil, errors.New("No layer found")
return nil, errors.New("No layer found")
}
if tc.Zoom > 13 || tc.Zoom < 1 {
return nil, nil, errors.New("Invalid zoom")
return nil, errors.New("Invalid zoom")
}
if tc.Zoom == 13 {
@ -129,17 +149,17 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
}
log.WithFields(fields).Debug("mapblock render from tilerender")
return nil, nil, err
return nil, err
}
if img == nil {
return nil, nil, nil
return nil, nil
}
buf := new(bytes.Buffer)
png.Encode(buf, img)
return img, buf.Bytes(), nil
return img, nil
}
//zoom 1-12
@ -155,24 +175,24 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
start := time.Now()
upperLeft, _, err := tr.renderImage(quads.UpperLeft, recursionDepth-1)
upperLeft, err := tr.renderImage(quads.UpperLeft, recursionDepth-1)
if err != nil {
return nil, nil, err
return nil, err
}
upperRight, _, err := tr.renderImage(quads.UpperRight, recursionDepth-1)
upperRight, err := tr.renderImage(quads.UpperRight, recursionDepth-1)
if err != nil {
return nil, nil, err
return nil, err
}
lowerLeft, _, err := tr.renderImage(quads.LowerLeft, recursionDepth-1)
lowerLeft, err := tr.renderImage(quads.LowerLeft, recursionDepth-1)
if err != nil {
return nil, nil, err
return nil, err
}
lowerRight, _, err := tr.renderImage(quads.LowerRight, recursionDepth-1)
lowerRight, err := tr.renderImage(quads.LowerRight, recursionDepth-1)
if err != nil {
return nil, nil, err
return nil, err
}
t := time.Now()
@ -186,29 +206,10 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
},
)
rect := image.Rect(0, 0, 128, 128)
if upperLeft != nil {
resizedImg := imaging.Resize(upperLeft, 128, 128, imaging.Lanczos)
draw.Draw(img, rect, resizedImg, image.ZP, draw.Src)
}
rect = image.Rect(128, 0, 256, 128)
if upperRight != nil {
resizedImg := imaging.Resize(upperRight, 128, 128, imaging.Lanczos)
draw.Draw(img, rect, resizedImg, image.ZP, draw.Src)
}
rect = image.Rect(0, 128, 128, 256)
if lowerLeft != nil {
resizedImg := imaging.Resize(lowerLeft, 128, 128, imaging.Lanczos)
draw.Draw(img, rect, resizedImg, image.ZP, draw.Src)
}
rect = image.Rect(128, 128, 256, 256)
if lowerRight != nil {
resizedImg := imaging.Resize(lowerRight, 128, 128, imaging.Lanczos)
draw.Draw(img, rect, resizedImg, image.ZP, draw.Src)
}
resizeImage(upperLeft, img, 0, 0)
resizeImage(upperRight, img, SUB_IMG_SIZE, 0)
resizeImage(lowerLeft, img, 0, SUB_IMG_SIZE)
resizeImage(lowerRight, img, SUB_IMG_SIZE, SUB_IMG_SIZE)
t = time.Now()
quadresize := t.Sub(start)
@ -242,5 +243,5 @@ func (tr *TileRenderer) renderImage(tc *coords.TileCoords, recursionDepth int) (
tr.Eventbus.Emit(eventbus.TILE_RENDERED, tc)
return img, buf.Bytes(), nil
return img, nil
}

View File

@ -72,14 +72,11 @@ func BenchmarkTileRender(b *testing.B) {
for n := 0; n < b.N; n++ {
data, err := tr.Render(coord)
err := tr.Render(coord)
if err != nil {
panic(err)
}
if data == nil {
panic("no data")
}
}
}

View File

@ -1,7 +1,6 @@
package tilerenderer
import (
"bytes"
"io/ioutil"
"mapserver/colormapping"
"mapserver/coords"
@ -69,30 +68,16 @@ func TestTileRender(t *testing.T) {
}
coord := coords.NewTileCoords(0, 0, 12, 0)
data, err := tr.Render(coord)
err = tr.Render(coord)
if err != nil {
panic(err)
}
if data == nil {
panic("no data")
}
f, _ := os.Create("../test-output/0_0_12.png")
bytes.NewReader(data).WriteTo(f)
coord1 := coord.GetZoomedOutTile()
data, err = tr.Render(coord1)
err = tr.Render(coord1)
if err != nil {
panic(err)
}
if data == nil {
panic("no data")
}
f, _ = os.Create("../test-output/0_0_13.png")
bytes.NewReader(data).WriteTo(f)
}

View File

@ -19,7 +19,7 @@ func worker(ctx *app.App, coords <-chan *coords.TileCoords, done chan bool) {
}
logrus.WithFields(fields).Debug("Tile render job tile")
_, err := ctx.Tilerenderer.Render(tc)
err := ctx.Tilerenderer.Render(tc)
if err != nil {
fields := logrus.Fields{
"X": tc.X,