Skip to main content

An influence map generator

Project description

bluemap - Influence map generator

PyPI - Version PyPI - Downloads PyPI - Python Version Documentation

GitHub Actions Workflow Status GitHub last commit GitHub issues

Bluemap is an influence map generator for games like Eve Online/Echoes. It is based on the algorithm from Paladin Vent (which was continued by Verite Rendition), but was written from scratch in C++ and Cython. It is designed to be faster and easier to use.

While the algorithm for the influence layer itself stayed the same, it was not a stable implementation as the final output depended on the processing order. Which, when using structures like maps or sets, is not guaranteed to be the same every time. It is especially different between the legacy Java implementation and the new C++ one. Also, this was an issue because the actual ids of owners and systems change how the final output looks like. So the original recursive DFS algorithm was replaced with an iterative BFS algorithm to ensure the same output every time. The legacy algorithm can be found at the legacy-algorithm tag.

The python API documentation can be found here.

This project is still work in progress. The API might change until a stable version is released. If you decide to already use it, please make sure to pin the version in your requirements.txt file. Until version 1.0.0 is released, minor versions might contain breaking changes. I will try to keep the changes as minimal as possible, but I cannot guarantee that there will be no breaking changes.

If you find a bug or have a feature request, please open an issue on GitHub.

Overview

As stated before, this project is implemented in C++ and Cython. The C++ part is responsible for the rendering of the influence layer, and the calculation of the owner label positions. All other parts are implemented in Cython and Python.

The C++ library does work in general standalone, but except for a testing tool that requires a specific file format as input, there is no real way to use it directly. So you would have to write your own wrapper around it, which loads the data from some source.

Example Map

Installation

PyPi has precompiled wheels for Windows (64bit), Linux and macOS (min version 14.0, untested). 32bit Windows is supported but not automated. PyPi may or may not have a precompiled wheel for 32bit Windows.

We support Python 3.12 and higher (atm 3.12 and 3.13 on PyPi)

The precompiled package can be installed via pip. There are multiple variations that can be installed via pip:

Name Map Tables MySQL DB
bluemap[minimal]
bluemap[table]
bluemap[CLI]

e.g. to install the full version, you can use the following command:

pip install bluemap[CLI]
  • Map: The module for rendering the influence map
  • Tables: The module for rendering tables (depends on Pillow)
  • MySQL DB: The module for loading data from a MySQL database (depends on pymysql)

Also note all functionality is available in the bluemap package. The extras are only for the convenience of the installation. You can also install the base version and add the dependencies manually.

Usage (CLI)

The CLI supports rendering of maps with data from a mysql database. The program will create all required tables on the first run. However, you do have to populate the tables yourself. You can find the static data for Eve Online on the UniWIKI. For the sovereignty data, you need to use the ESI API.

Arg Description
--help,-h Show the help message
--host HOST The host of the db
--user USER The user for the db
--password PASSWORD The password for the db (empty for no password)
--database DATABASE The database to use
--text [header] TEXT Extra text to render (see below)
--output,-o OUTPUT The output file for the image
--map_out,-mo PATH The output file for the map data
--map_in,-mi PATH The input file for the old map data

The database args are all required, the other ones are optional. map_in and map_out are used for the rendering of changed influence areas. If the old map is provided, in areas where the influence changed, the old influence will be rendered as diagonal lines. These files in principle simply store the id of the owner for every pixel. Please refer to the implementation for the exact format.

The text argument is used to render additional text in the top left corner. This argument may be repeated multiple times for multiple lines of text. There are three ways to use this

  1. --text "Some text": This will render the text in the default font
  2. --text header "Some text": This will render the text in the header font (bold)
  3. --text: This will render an empty line (but an empty string would also work)

(all three ways can be chained for multiple lines)

Example usage:

python -m bluemap.main \
       --host localhost \
       --user root \
       --password "" \
       --database evemap \
       -o influence.png \
       -mi sovchange_2025-02-16.dat \
       -mo sovchange_2025-02-23.dat \
       --text header "Influence Map" \
       --text \
       --text "Generated by Blaumeise03"

Usage (Library)

The library is very simple to use. You can find an example inside the main.py file. The main class is the SovMap class. This does all the heavy lifting. The load_data method is used to load the data into the map.

Please note that the API is subject to change until a stable version is released. I recommend pinning the version in your requirements.txt file and manually update it.

🚨 IMPORTANT 🚨: Before you use any of the SovMap.set_XYZ_function methods, read the Customization section below. Otherwise, you may run into memory leaks.

from bluemap import SovMap

sov_map = SovMap()

sov_map.load_data(
    owners=[{
        'id': 10001,
        'color': (0, 255, 0),
        'name': 'OwnerA',
        'npc': False,
    }],
    systems=[
        {
            'id': 20001, 'name': 'Jita',
            'constellation_id': 30001, 'region_id': 40001,
            'x': -129064e12, 'y': 60755e12, 'z': -117469e12,
            'has_station': True,
            'sov_power': 6.0,
            'owner': 10001,
        }, {'id': 20002, 'name': ...}
    ],
    connections=[
        (20001, 20002),
    ],
    regions=[{'id': 40001, 'name': 'The Forge',
              'x': -96420e12, 'y': 64027e12, 'z': -112539e12},
             ],
    filter_outside=True, # Will skip all systems outside the map
)

For the rendering, please refer to the render method inside the main.py. You can see the usage with documentation there.

Rendering

Some more special methods. First of all, the rendering is implemented in C++ and does not interact with Python. Therefore, it can be used with Python's multithreading. In general, all methods are thread safe. But any modifications to the map are blocked as long as any thread is rendering. The rendering will split the map into columns, every thread will render one column. There is a default implementation inside _map.pyx:

from bluemap import SovMap, ColumnWorker
sov_map = SovMap()
if not sov_map.calculated:
    sov_map.calculate_influence()
from concurrent.futures.thread import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=16) as pool:
    workers = sov_map.create_workers(16)
    pool.map(ColumnWorker.render, workers)

But please make sure, two ColumnWorkers who overlap are not thread safe. The create_workers method will generate disjoint workers. But if you call the method multiple times, you have to make sure the workers are disjointed. See the source code of the SovMap.render functions for more information.

Tables

The module bluemap.table contains classed for rendering of tables. This requires the Pillow package. Please refer to the example inside the main.py file on how to use it.

Algorithm Details

The algorithm is based on the algorithm from Paladin Vent. It has two steps: The spreading of influence over neighbored systems, and the actual calculation of influence per pixel. Every system starts with an optional owner and a power value.

Influence Spreading

In the first step, this initial power is spread over the neighbors via the connections. The initial power is first converted into its actual power used for the algorithm. That was done because in the original implementation, the power input was simply the ADM level of the system which needed to be converted to yield nicer results. This power_function can be modified by the user. The default implementation is:

def power_function(
        sov_power: float,
        has_station: bool,
        owner_id: int) -> float:
    return 10.0 * (6 if sov_power >= 6.0 else sov_power / 2.0)

Any function that matches this signature can be used. In the C++ implementation, this can be done by providing a matching std::function. When compiled against CPython (which happens in the PyPi builds), a python callable may be provided via SovMap.set_sov_power_function.

The spreading of the influence is done via a BFS algorithm. The influence is spread over the connections to the neighbored systems. For every jump, the influence is being reduced. By default, the influence is reduced to 30% of the previous value. This can be modified as well:

def power_falloff_function(
        value: float,
        base_value: float,
        distance: int) -> float:
    return value * 0.3

This function is evaluated every time the distance is increased. The value is the current influence, the base_value is the original influence and the distance is the current distance from the source system. If the falloff returns 0 or less, the algorithm will stop spreading the influence. This can be used to limit the influence to a certain range.

This is WIP, at the moment there is still a hard limit of two jumps (three for systems with an ADM of 6 or above).

For every system, a map of owners with their accumulated influence is created.

Influence Aggregation

The second step is done on the image itself for every pixel, the topology of the map is no longer relevant. For every pixel, the influences of all systems in a 400 pixel radius are accumulated. The influence is weighted by the following formula: power / (500 + dist_sq). This is done for every owner for every system. The owner with the highest influence is considered the owner of the pixel and will be rendered in the final image.

The influence is rendered as the color of the owner, with the alpha channel representing the influence according to the following function:

import math


def influence_to_alpha(influence: float) -> float:
    return float(min(
        190,
        int(math.log(math.log(influence + 1.0) + 1.0) * 700)))

Additionally, the borders of the influence areas are rendered with a stronger color (higher alpha value).

Old Owner Overlay

The algorithm does generate two images. The first one is the RGBA image itself. But additionally, a second image containing the owner id for every pixel is generated. This image can be provided the next time to the algorithm to highlight areas where the influence changed. The old owner will be rendered as diagonal lines in the final image.

Customization

As stated before, a lot of functions for the rendering can be customized. The default functions are implemented in C++ are really fast. Replacing them with Python functions adds considerable overhead. For the influence spreading, this is not a big issue, as this is pretty fast anyway and does not scale with the size of the image. But for the influence aggregation, this can be a problem. Simply replacing the C++ functions with Python functions will double the rendering time.

It is planned to provide a more efficient way of specifying simple mathematical expressions a strings, which get compiled into a callable that does not hook into python. This is not implemented yet.

All objects that are a function, a bound method, or a callable (that implements __call__) can be used as a function. For bound methods, the self argument is allowed in the signature.

🚨 IMPORTANT 🚨: Do not provide methods of the SovMap class (or ony inherited class) as a function. This will cause a circular reference and will prevent the garbage collector from collecting the object. The underlying C++ object will hold a reference to the callable and the SovMap class holds a reference to the C++ class. If a bound method is provided (which holds a reference to the bound object), we have a cycle. I do not think the garbage collector is able to infer that this is a cycle and thus will never collect the object.

Alternatively, you can implement a wrapper function that holds a weak reference to the callable. As long as you ensure that the callable is not deallocated while the SovMap is in use, this should work.

The return types of the functions are strict. The functions must exactly return the type they are supposed to return.

One last thing that can be customized is the automatic color generation. If the algorithm tries to render an owner that does not yet have a color assigned, it will generate a new color. By default, the SovMap class has this function implemented (SovMap.next_color). But another function may be passed, it must have this signature:

def next_color(owner_id: int) -> tuple[int, int, int]:
    pass

But please note, the set_generate_owner_color_function does only affect the rendering of the influence layer. The function SovMap.draw_systems does also generate colors for owners, but it will always use the next_color function from the SovMap class.

Building

Python

On windows, this project requires the MSVC compiler. The library requires at least C++17, if you use a different compiler on windows, you will have to modify the extra_compile_args inside the setup.py file.

Also, this project requires Python 3.12 or higher. I have not tested it with lower versions, but the C++ code gets compiled against CPython and uses features from the C-API that require Python 3.12. That being said, this is technically speaking not required. You could disable the C-API usage, but at the moment, you would have to remove the functions from the Cython code that depend on the C-API.

Compiling can either happen via

python -m build

or, if you want to build it in-place

python setup.py build_ext --inplace

this will also generate .html files for an analysis of the Cython code.

Standalone

This project has a small CMakelists.txt file that can be used to compile the C++ code as a standalone executable. It does download std_image_write from GitHub to write the png image. However, as I have mentioned, the C++ code has no nice way to load the data. Refer to Map::load_data inside the Map.cpp file for the required format.

Credits

The original algorithm was created by Paladin Vent and continued by Verite Rendition. Verite's version can be found at https://www.verite.space/. I do not know if Paladin Vent has a website (feel free to contact me to add it here). The original algorithm was written in Java and can be found on Verite's website.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bluemap-0.1.0a1.tar.gz (358.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

bluemap-0.1.0a1-cp313-cp313-win_amd64.whl (563.3 kB view details)

Uploaded CPython 3.13Windows x86-64

bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ x86-64

bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_i686.whl (3.6 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ i686

bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl (2.4 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ i686

bluemap-0.1.0a1-cp313-cp313-macosx_14_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.13macOS 14.0+ ARM64

bluemap-0.1.0a1-cp312-cp312-win_amd64.whl (564.4 kB view details)

Uploaded CPython 3.12Windows x86-64

bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_i686.whl (3.6 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ i686

bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl (2.4 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ i686

bluemap-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

File details

Details for the file bluemap-0.1.0a1.tar.gz.

File metadata

  • Download URL: bluemap-0.1.0a1.tar.gz
  • Upload date:
  • Size: 358.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.3

File hashes

Hashes for bluemap-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 5a8e59f6644ecd685fd44aeed932829fa4c159ff7972997e51f1e0cfbb8d90f5
MD5 192993a2a0956a84fc62273ea1e58f29
BLAKE2b-256 a67d7fe4215e8d4e7ec2796fd89dcbd141351e9dc1b6c841aa569f0ac0303b19

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: bluemap-0.1.0a1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 563.3 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.3

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 5d753986dbca243aa0f88a6df01f9baa82b8a22d9372c666eeb9dee474fe1748
MD5 9de990a0c5c7ea16c49b760204fd21ec
BLAKE2b-256 61621640a504c74bea4f2bde54d5fbb2b6a32afc3093c95680a67b2479854e7a

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 974e3f4aeafd2836c2da4d2a92ecb4d788e905010d075169066573c7995c446f
MD5 d4eab8693ded2cc0b7256bfc1a62e02a
BLAKE2b-256 3f32083baee47db922d765cf07e9eb26b25820f671012098e8f5bdadfafb5d55

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 6f5fe7ef1b4df01d41e9d99054a8713f2b36fb8bf7048bf11ebe8e54b22a690a
MD5 7c03aab43baefa6d342fe5ced38b3a88
BLAKE2b-256 82e3f87089fba8d53e18035d5fce5eea2c596af7d9abf5ea0c8439cba8849e58

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3c216d4fc3f42495b6cb561f00bd4c15c3c836ac9e16d5bbe2c4cf201abfb483
MD5 4c7c74cc098e2660a8d14aa23a1ff45f
BLAKE2b-256 c282bf1157dcd572e4190685a2eae247a735e0226248dbfde381a4ceb4930f30

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 b5fa10cdcf48a19618601f8023fec493214a7e3e8c8ad18ec5f9a1fa78d034ca
MD5 9432706bf12aab05eb6e5488fa4b0647
BLAKE2b-256 879f61415414924d8f509bf4cd1468271c7afff383fa4cf3a50d16cbde85e34c

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp313-cp313-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp313-cp313-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 797c70171fd146888b41932fbeb8e171d1a379881c4d6192bdc62b7acbe5d21c
MD5 bc5147b89ebfe7330e75e09fb05f8843
BLAKE2b-256 18eaf231290bb286e01bf61f7812d95ea4211c481d3d7f6cbd6f9a9b38e4d484

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: bluemap-0.1.0a1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 564.4 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.3

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 7e75534dd7a26189cc87ac3e863e0a72206a9e134af8d93b85b8b0261706a5e6
MD5 9104a3336bd410313bc430a7470cf191
BLAKE2b-256 1a0db7e2c3d41e064f18e7e32c49448212ba75aeab5d29a6cabd6eb7fc6babb2

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 ea5fbb46716193f1ba5c85d730944f68c2d7a29ee258cd6d596fc93bf000f36f
MD5 80fee2677b3c57d700c55e3821a74618
BLAKE2b-256 770984431ca3112fc092f8885298128c1ae6e7c808bcbd0befc0fea14a1c503b

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 4c346b5e00aa669404f604b6a271adcfeaffc964b12747129991b0adf164016e
MD5 69aa466fae490c8dd512084bb3b7de60
BLAKE2b-256 0467752c3126147b23591b2e8cec6f1f81a66aa6672c47a315e0153e69b7ed3d

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 fa5a80262674586cb75f504da6d83409402261258211000016a674931102a31c
MD5 f6e1f071bcc731ef795ad47ea3c90e5e
BLAKE2b-256 951cd1d7f8f860d009e1e65d22cb816272e24d9be2ab23ce00bed1f58b0de32d

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 dac7aad76ef47cde9f6e24abca7aa90a528ced7ee1b2bed3624d4c414d726475
MD5 9a127913d356678a73a4ad07c4be933f
BLAKE2b-256 409d8e5b38c8457057cde9f7985ff86a29a2684e1e723126a6e7770faefe7dca

See more details on using hashes here.

File details

Details for the file bluemap-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for bluemap-0.1.0a1-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 2a68626a7f7941a59f501e7feb80abd279fa00260b7e543d4cd355c0f658e904
MD5 315d5d5d6036696180034a8c6df85039
BLAKE2b-256 563f4f029ad17b297171a133de6245588442ad18f98b05d690d4467c80dd058f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page