I can share the function that we use to generate the offset on our side but please note that this is a very custom solution that requires a lot of other components to be in place. What we do is to transform our documents in the main database into spatial entities in a postgresql with POSTGIS on create/update/delete operations. The tile server is connected to the PG database and will use postgis spatial functions to create geometries which are then combined into a single requested MVT (PBF) binary tile that the client requested. We don't create geojson output here.
This is the function to create one of the offsets - this has to be adjusted/refined to also have functions for higher resolsutions, zoom levels etc, but I think this will show the idea of how it works:
getQueryAirspacesBorderOffset({ zoom, sqlEnvelope }) {
let buffer = -0.003;
if (zoom >= 13) buffer = -0.004;
return `WITH
bounds AS ( SELECT ${sqlEnvelope} AS geom, ${sqlEnvelope}::box2d AS b2d ),
mvtgeom AS (
SELECT ST_asMVTGeom(ST_Transform(ST_Difference(geometry, ST_Buffer(geometry, ${buffer} )), 3857), bounds.b2d) AS geom,
source_id,
feature_type,
type,
icao_class,
country,
on_request,
on_demand,
by_notam,
special_agreement,
active_from,
active_until,
(hours_of_operation #>> '{}')::json as hours_of_operation
FROM airspace_features t, bounds
WHERE ST_Intersects(t.geometry, ST_Transform(bounds.geom, 4326))
)
SELECT ST_AsMVT(mvtgeom.*, 'airspaces_border_offset') FROM mvtgeom`;
}
As I said, this is only part of a bigger very complex pipeline that in the end outputs a PBF tile. It not only requires a lot of code but also to have the required infrastructure (posrgresql + POSGIS) in place. Personally I think this is only worth the effort if you really need a separate custom tuned MVT tile service. I would advise to not go down this route since it takes quite some effort to get this running. Even now, after months, we still refine things...
Personally, if it is possible to do so, I would offload the creation of the geojson either completely to the client (if the app uses preloaded geojson files) or an intermediate server that outputs geojson data that is created using the original openaip data (i.e. the source airspace polygons). For JS, there is the spatial library turfjs that we use extensively and it is also capable of creating those geometries. If you are in the JS world, this is definitely worth a look!
Hope this helps a bit.
Cheers,
Stephan